<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="keywords" content="玖忆;文鹤;博客">
    
    <meta name="author" content="meteor">
    <!-- preconnect -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    
    
    <!--- Seo Part-->
    
    <link rel="canonical" href="https://wait-you.github.io/2023/06/06/jvm/"/>
    <meta name="robots" content="index,follow">
    <meta name="googlebot" content="index,follow">
    <meta name="revisit-after" content="1 days">
    
        <meta name="description" content="# JVM # 引言 # 什么是 JVM # 定义 Java Virtual Machine - java 程序的运行环境（java 二进制字节码的运行环境） # 好处  一次编写，导出运行  jvm 屏蔽了字节码和底层操作系统的差异，对外提供了一致的运行环境   自动内存管理，垃圾回收功能 数组下标越界检查 多态  # 比较 jvm jre jdk  ​ # 学习路线  # 内存结构 # 程序计">
<meta property="og:type" content="article">
<meta property="og:title" content="JVM">
<meta property="og:url" content="https://wait-you.github.io/2023/06/06/JVM/index.html">
<meta property="og:site_name" content="玖忆">
<meta property="og:description" content="# JVM # 引言 # 什么是 JVM # 定义 Java Virtual Machine - java 程序的运行环境（java 二进制字节码的运行环境） # 好处  一次编写，导出运行  jvm 屏蔽了字节码和底层操作系统的差异，对外提供了一致的运行环境   自动内存管理，垃圾回收功能 数组下标越界检查 多态  # 比较 jvm jre jdk  ​ # 学习路线  # 内存结构 # 程序计">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220218195638285.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220218200452408.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220221142729070.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220226195015543.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220226195035365.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220226203108552.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220228125439542.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220228125709844.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220228131040558.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220228135050354.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220228151826294.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220228152400678.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220228160055673.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220301083352858.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220301083706505.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220301083804132.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220301084022984.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220301084507087.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220301085135007.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220301092711562.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220301133805597.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220301140303919.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220301140328043.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220304104332861.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220305152956911.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220306085831834.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220306143623411.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220306143959628.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220306150058186.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220306150235738.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220306150432081.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20220306171823369.png">
<meta property="article:published_time" content="2023-06-06T00:45:23.000Z">
<meta property="article:modified_time" content="2023-06-05T08:45:47.483Z">
<meta property="article:author" content="meteor">
<meta property="article:tag" content="JVM">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://tuchuang.wenhe9.cn/image-20220218195638285.png">
    
    
    <!--- Icon Part-->
    <link rel="icon" type="image/png" href="/images/wenhe.png" sizes="192x192">
    <link rel="apple-touch-icon" sizes="180x180" href="/images/wenhe.png">
    <meta name="theme-color" content="#f1404b">
    <link rel="shortcut icon" href="/images/wenhe.png">
    <!--- Page Info-->
    
    <title>
        
            JVM -
        
        玖忆
    </title>
    
<link rel="stylesheet" href="/css/style.css">

    
<link rel="stylesheet" href="/assets/fonts.css">

    <!--- Font Part-->
    
    
    
    

    <!--- Inject Part-->
    
    <script id="hexo-configurations">
    let Global = window.Global || {};
    Global.hexo_config = {"hostname":"wait-you.github.io","root":"/","language":"zh-CN"};
    Global.theme_config = {"articles":{"style":{"font_size":"16px","line_height":1.5,"image_border_radius":"14px","image_alignment":"center","image_caption":false,"link_icon":true},"word_count":{"enable":true,"count":true,"min2read":true},"author_label":{"enable":true,"auto":false,"list":[]},"code_block":{"copy":true,"style":"mac","font":{"enable":false,"family":null,"url":null}},"toc":{"enable":true,"max_depth":3,"number":false,"expand":true,"init_open":true},"copyright":true,"lazyload":true,"recommendation":{"enable":false,"title":"推荐阅读","limit":3,"placeholder":"http://tuchuang.wenhe9.cn/default-bg.jpg","skip_dirs":[]}},"colors":{"primary":"#f1404b","secondary":null},"global":{"fonts":{"chinese":{"enable":false,"family":null,"url":null},"english":{"enable":false,"family":null,"url":null}},"content_max_width":"1000px","sidebar_width":"210px","hover":{"shadow":true,"scale":false},"scroll_progress":{"bar":true,"percentage":true},"busuanzi_counter":{"enable":true,"site_pv":true,"site_uv":true,"post_pv":true},"pjax":true,"open_graph":true,"google_analytics":{"enable":false,"id":null}},"home_banner":{"enable":true,"style":"static","image":{"light":"http://tuchuang.wenhe9.cn/default-bg.jpg","dark":"http://tuchuang.wenhe9.cn/default-bg.jpg"},"title":"玖忆","subtitle":{"text":["我本微末凡尘、可也心向天空"],"hitokoto":{"enable":false,"api":"https://v1.hitokoto.cn"},"typing_speed":100,"backing_speed":80,"starting_delay":500,"backing_delay":1500,"loop":true,"smart_backspace":true},"text_color":{"light":"#fff","dark":"#d1d1b6"},"text_style":{"title_size":"2.8rem","subtitle_size":"1.5rem","line_height":1.2},"custom_font":{"enable":false,"family":null,"url":null},"social_links":{"enable":true,"links":{"github":"https://gitee.com/du-jinliang","instagram":null,"zhihu":null,"twitter":null,"email":"dujinliang9@163.com"}}},"plugins":{"feed":{"enable":false},"aplayer":{"enable":false,"type":"fixed","audios":[{"name":null,"artist":null,"url":null,"cover":null}]},"mermaid":{"enable":false,"version":"9.3.0"}},"version":"2.1.4","navbar":{"auto_hide":true,"color":{"left":"#f78736","right":"#367df7","transparency":35},"links":{"Home":{"path":"/","icon":"fa-regular fa-house"}},"search":{"enable":false,"preload":true}},"page_templates":{"friends_column":2,"tags_style":"blur"},"home":{"sidebar":{"enable":true,"position":"left","first_item":"menu","announcement":null,"links":null},"article_date_format":"auto","categories":{"enable":true,"limit":3},"tags":{"enable":true,"limit":3}}};
    Global.language_ago = {"second":"%s 秒前","minute":"%s 分钟前","hour":"%s 小时前","day":"%s 天前","week":"%s 周前","month":"%s 个月前","year":"%s 年前"};
    Global.data_config = {"masonry":false};
  </script>
    
    <!--- Fontawesome Part-->
    
<link rel="stylesheet" href="/fontawesome/fontawesome.min.css">

    
<link rel="stylesheet" href="/fontawesome/brands.min.css">

    
<link rel="stylesheet" href="/fontawesome/solid.min.css">

    
<link rel="stylesheet" href="/fontawesome/regular.min.css">

    
    
    
    
<meta name="generator" content="Hexo 6.3.0"></head>


<body>
<div class="progress-bar-container">
    
        <span class="scroll-progress-bar"></span>
    

    
        <span class="pjax-progress-bar"></span>
        <span class="pjax-progress-icon">
            <i class="fa-solid fa-circle-notch fa-spin"></i>
        </span>
    
</div>


<main class="page-container">

    

    <div class="main-content-container">

        <div class="main-content-header">
            <header class="navbar-container">
    
    <div class="navbar-content">
        <div class="left">
            
            <a class="logo-title" href="/">
                
                玖忆
                
            </a>
        </div>

        <div class="right">
            <!-- PC -->
            <div class="desktop">
                <ul class="navbar-list">
                    
                        
                            <li class="navbar-item">
                                <!-- Menu -->
                                <a class="" 
                                    href="/"  >
                                    
                                        
                                            <i class="fa-regular fa-house"></i>
                                        
                                        首页
                                    
                                </a>
                                <!-- Submenu -->
                                
                            </li>
                    
                    
                </ul>
            </div>
            <!-- Mobile -->
            <div class="mobile">
                
                <div class="icon-item navbar-bar">
                    <div class="navbar-bar-middle"></div>
                </div>
            </div>
        </div>
    </div>

    <!-- Mobile drawer -->
    <div class="navbar-drawer">
        <ul class="drawer-navbar-list">
            
                
                    <li class="drawer-navbar-item flex-center">
                        <a class="" 
                        href="/"  >
                             
                                
                                    <i class="fa-regular fa-house"></i>
                                
                                首页
                            
                        </a>
                    </li>
                    <!-- Submenu -->
                    
            

        </ul>
    </div>

    <div class="window-mask"></div>

</header>


        </div>

        <div class="main-content-body">

            

            <div class="main-content">

                
                    <div class="fade-in-down-animation">
    <div class="post-page-container">
        <div class="article-content-container">

            
            
                <div class="article-title">
                    <h1 class="article-title-regular">JVM</h1>
                </div>
            
                
            

            
                <div class="article-header">
                    <div class="avatar">
                        <img src="/images/wenhe.png">
                    </div>
                    <div class="info">
                        <div class="author">
                            <span class="name">meteor</span>
                            
                                <span class="author-label">Lv3</span>
                            
                        </div>
                        <div class="meta-info">
                            <div class="article-meta-info">
    <span class="article-date article-meta-item">
        <i class="fa-regular fa-pen-fancy"></i>&nbsp;
        <span class="desktop">2023-06-05 16:45:23</span>
        <span class="mobile">2023-06-05 16:45</span>
        <span class="hover-info">创建</span>
    </span>
    
        <span class="article-date article-meta-item">
            <i class="fa-regular fa-wrench"></i>&nbsp;
            <span class="desktop">2023-06-05 00:45:47</span>
            <span class="mobile">2023-06-05 00:45</span>
            <span class="hover-info">更新</span>
        </span>
    

    
        <span class="article-categories article-meta-item">
            <i class="fa-regular fa-folders"></i>&nbsp;
            <ul>
                
                    <li>
                        <a href="/categories/Java/">Java</a>&nbsp;
                    </li>
                
            </ul>
        </span>
    
    
        <span class="article-tags article-meta-item">
            <i class="fa-regular fa-tags"></i>&nbsp;
            <ul>
                
                    <li>
                        <a href="/tags/JVM/">JVM</a>&nbsp;
                    </li>
                
            </ul>
        </span>
    

    
    
    
    
        <span class="article-pv article-meta-item">
            <i class="fa-regular fa-eye"></i>&nbsp;<span id="busuanzi_value_page_pv"></span>
        </span>
    
</div>

                        </div>
                    </div>
                </div>
            

            <div class="article-content markdown-body">
                <h1 id="jvm"><a class="markdownIt-Anchor" href="#jvm">#</a> JVM</h1>
<h2 id="引言"><a class="markdownIt-Anchor" href="#引言">#</a> 引言</h2>
<h3 id="什么是jvm"><a class="markdownIt-Anchor" href="#什么是jvm">#</a> 什么是 JVM</h3>
<h4 id="定义"><a class="markdownIt-Anchor" href="#定义">#</a> 定义</h4>
<p>Java Virtual Machine - java 程序的运行环境（java 二进制字节码的运行环境）</p>
<h4 id="好处"><a class="markdownIt-Anchor" href="#好处">#</a> 好处</h4>
<ul>
<li>一次编写，导出运行
<ul>
<li>jvm 屏蔽了字节码和底层操作系统的差异，对外提供了一致的运行环境</li>
</ul>
</li>
<li>自动内存管理，垃圾回收功能</li>
<li>数组下标越界检查</li>
<li>多态</li>
</ul>
<h4 id="比较"><a class="markdownIt-Anchor" href="#比较">#</a> 比较</h4>
<p>jvm jre jdk</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220218195638285.png"
                      alt="image-20220218195638285"
                ></p>
<p>​</p>
<h3 id="学习路线"><a class="markdownIt-Anchor" href="#学习路线">#</a> 学习路线</h3>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220218200452408.png"
                      alt="image-20220218200452408"
                ></p>
<h2 id="内存结构"><a class="markdownIt-Anchor" href="#内存结构">#</a> 内存结构</h2>
<h3 id="程序计数器"><a class="markdownIt-Anchor" href="#程序计数器">#</a> 程序计数器</h3>
<h4 id="定义-2"><a class="markdownIt-Anchor" href="#定义-2">#</a> 定义</h4>
<p>Program Counter Register 程序计数器（寄存器）</p>
<ul>
<li>
<p>作用</p>
<ul>
<li>记住下一条 jvm 指令的执行地址</li>
</ul>
</li>
<li>
<p>特点</p>
<ul>
<li>
<p>是线程私有的</p>
</li>
<li>
<p>不会存在内存溢出</p>
</li>
</ul>
</li>
</ul>
<h3 id="虚拟机栈"><a class="markdownIt-Anchor" href="#虚拟机栈">#</a> 虚拟机栈</h3>
<h4 id="定义-3"><a class="markdownIt-Anchor" href="#定义-3">#</a> 定义</h4>
<p>Java Virtual Machine Stacks (Java 虚拟机栈)</p>
<ul>
<li>每个线程运行时所需要的内存，称为虚拟机栈</li>
<li>每个栈由多个栈帧（Frame）组成，对应着每次方法调用时所占用的内存</li>
<li>每个线程只能有一个活动栈帧，对应着当前正在执行的那个方法</li>
</ul>
<p><code>问题辨析</code></p>
<ul>
<li>垃圾回收是否涉及栈内存？</li>
</ul>
<blockquote>
<p>栈内存是一次次的方法调用产生的栈桢内存，而栈桢内存在方法调用结束后都会被弹出栈，也就是会自动的被回收掉，所以不需要垃圾回收管理栈内存</p>
</blockquote>
<ul>
<li>栈内存的分配越大越好吗？</li>
</ul>
<blockquote>
<p>栈内存划分的越大，反而会让线程数越少，因为物理内存是一定的，比如，物理内存是 500M, 一个线程使用的是 1M 的栈内存，那么可以开启 500 个线程，如果一个线程使用的是 2M 的栈内存，那么只可以开启 250 个栈内存。所以栈内存不是越大越好，划分的大了，通常只是能够进行更多次的方法递归调用，而不会增强程序的运行效率</p>
</blockquote>
<ul>
<li>方法内的局部变量是否线程安全？</li>
</ul>
<blockquote>
<p>看一个变量是否是线程安全的，其实就看多个线程对这个变量是共享的，还是这个变量对每个线程是私有的。如果方法内局部变量没有逃离方法的作用访问，他是线程安全的。如果是局部变量引用了对象，并逃离方法的作用方法，需要考虑线程安全</p>
</blockquote>
<h3 id="栈内存溢出"><a class="markdownIt-Anchor" href="#栈内存溢出">#</a> 栈内存溢出</h3>
<ol>
<li><strong>栈桢过多导致栈内存溢出</strong></li>
<li><strong>栈桢过大导致栈内存溢出</strong></li>
</ol>
<h3 id="线程运行诊断"><a class="markdownIt-Anchor" href="#线程运行诊断">#</a> 线程运行诊断</h3>
<ul>
<li>案例 1 : cpu 占用过多
<ul>
<li>定位
<ul>
<li>用 <code>top H -eo pid,tid,%cpu | grep 进程id</code> （用 ps 命令进一步定位是哪一个线程引起的 cpu 占用过高）</li>
<li><code>jstack 进程id</code>
<ul>
<li>可以根据线程 id 找到有问题的线程，进一步定位到问题代码的源代码行号</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>案例 2：程序运行很长时间没有结果</li>
</ul>
<h3 id="本地方法栈"><a class="markdownIt-Anchor" href="#本地方法栈">#</a> 本地方法栈</h3>
<p>本地方法是指的是不是由 Java 代码编写的方法，因为 Java 代码有一定的限制，有时候不能够直接和操作系统底层打交道，所以需要用一些用 C/C++ 编写的本地方法和操作系统底层打交道，Java 代码可以间接的通过本地方法和来调用底层的功能，这些本地方法使用的内存就叫本地方法栈</p>
<p>给本地方法的运行提供一个内存空间</p>
<h3 id="堆"><a class="markdownIt-Anchor" href="#堆">#</a> 堆</h3>
<p>程序计数器、虚拟机栈、本地方法栈都是线程私有的</p>
<p>堆、方法区都是线程共享的区</p>
<h4 id="定义-4"><a class="markdownIt-Anchor" href="#定义-4">#</a> 定义</h4>
<p>Heap 堆</p>
<ul>
<li>通过 new 关键字，创建对象都会使用堆内存</li>
</ul>
<p>特点</p>
<ul>
<li>他是线程共享的，堆中对象都需要考虑线程安全问题</li>
<li>有垃圾回收机制</li>
</ul>
<h4 id="堆内存溢出"><a class="markdownIt-Anchor" href="#堆内存溢出">#</a> 堆内存溢出</h4>
<p>通过 <code>-Xmx8M</code>  改变堆大小</p>
<h4 id="堆内存诊断"><a class="markdownIt-Anchor" href="#堆内存诊断">#</a> 堆内存诊断</h4>
<ol>
<li>jps 工具
<ul>
<li>查看当前系统中有哪些 Java 进程</li>
</ul>
</li>
<li>jmap 工具
<ul>
<li>查看堆内存占用情况   <code>jmap -heap 进程id</code></li>
</ul>
</li>
<li>jconsole 工具
<ul>
<li>图形界面的，多功能的检测工具，可以连续监测</li>
</ul>
</li>
</ol>
<p>案例</p>
<ul>
<li>垃圾回收后，内存占用仍然很高
<ul>
<li>jps 查询 进程 id</li>
<li>jmap 查看内存占用情况</li>
<li>或 jconsole 查看内存占用情况</li>
<li>使用 jvirtulvm 堆转储查看对象使用情况</li>
</ul>
</li>
</ul>
<h3 id="方法区"><a class="markdownIt-Anchor" href="#方法区">#</a> 方法区</h3>
<h4 id="定义-5"><a class="markdownIt-Anchor" href="#定义-5">#</a> 定义</h4>
<p>所有 java 虚拟机线程共享的区</p>
<p>它存储了跟类的结构相关的信息：类的成员变量、方法数据：成员方法和构造方法、运行时常量池</p>
<p>方法区在虚拟机启动时被创建，逻辑上是堆的组成部分</p>
<p>方法区是规范，永久代和元空间是实现，不同的厂商实现不同</p>
<p>方法区也会导致内存溢出的错误</p>
<h4 id="组成"><a class="markdownIt-Anchor" href="#组成">#</a> 组成</h4>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220221142729070.png"
                      alt="image-20220221142729070"
                ></p>
<p>在 1.8 以前类的结构信息和常量池都在方法区（实现永久代）中，1.8 之后的字符串常量池在堆中，类的结构信息和常量池在不包括字符串常量池在系统内存（元空间）中</p>
<h4 id="内存溢出"><a class="markdownIt-Anchor" href="#内存溢出">#</a> 内存溢出</h4>
<p><code>-XX:MaxPermSize=8M</code>  设置永久代大小</p>
<p><code>-XX:MaxMetaspaceSize=8M</code>  设置元空间大小</p>
<p><code>Classloader</code>  类可以用来加载类的二进制字节码</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestMapper</span> <span class="keyword">extends</span> <span class="title class_">ClassLoader</span>&#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">TestMapper</span> <span class="variable">testMapper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TestMapper</span>();</span><br><span class="line">            <span class="keyword">for</span> (; j &lt; <span class="number">10000</span>; j++) &#123;</span><br><span class="line">                <span class="comment">//ClassWriter 作用是生成类的二进制字节码</span></span><br><span class="line">                <span class="type">ClassWriter</span> <span class="variable">cs</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassWriter</span>(<span class="number">0</span>);</span><br><span class="line">                <span class="comment">//版本号 public 类名 包名 父类 接口</span></span><br><span class="line">                cs.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, <span class="string">&quot;Class&quot;</span> + j, <span class="literal">null</span>, <span class="string">&quot;java/lang/Object&quot;</span>, <span class="literal">null</span>);</span><br><span class="line">                <span class="comment">//返回 byte[]</span></span><br><span class="line">                <span class="type">byte</span>[] code = cs.toByteArray();</span><br><span class="line">                <span class="comment">//执行类的加载</span></span><br><span class="line">                testMapper.defineClass(<span class="string">&quot;Class&quot;</span> + j, code, <span class="number">0</span>, code.length); <span class="comment">//class对象</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            System.out.println(j);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h3 id="运行时常量池"><a class="markdownIt-Anchor" href="#运行时常量池">#</a> 运行时常量池</h3>
<ul>
<li>常量池，就是一张表，虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息</li>
<li>运行时常量池，常量池是 *.class 文件中的，当该类被加载，他的常量池信息就会放入运行时常量池，并把里面的符号地址变为真实地址</li>
</ul>
<p>二进制字节码包括类基本信息、常量池、类方法定义包含虚拟机指令</p>
<p><code>javac HelloWorld.java</code>  编译</p>
<p><code>javap -v HelloWorld.class</code>  反编译</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloWorld</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;hello world&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line">Classfile /home/meteor/code/yygh-parent/service/service_hosp/src/test/java/cn/wenhe9/yygh/hosp/test/HelloWorld.<span class="keyword">class</span></span><br><span class="line">  <span class="title class_">Last</span> modified <span class="number">2022</span>年<span class="number">2</span>月<span class="number">21</span>日; size <span class="number">450</span> bytes</span><br><span class="line">  SHA-<span class="number">256</span> checksum 312583e7cc82948e86fee9a726d80a777ce140ca021a9ab44dcd6d238ad50381</span><br><span class="line">  Compiled from <span class="string">&quot;HelloWorld.java&quot;</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">cn</span>.wenhe9.yygh.hosp.test.HelloWorld</span><br><span class="line">  minor version: <span class="number">0</span></span><br><span class="line">  major version: <span class="number">61</span></span><br><span class="line">  flags: (<span class="number">0x0021</span>) ACC_PUBLIC, ACC_SUPER</span><br><span class="line">  this_class: #<span class="number">21</span>                         <span class="comment">// cn/wenhe9/yygh/hosp/test/HelloWorld</span></span><br><span class="line">  super_class: #<span class="number">2</span>                         <span class="comment">// java/lang/Object</span></span><br><span class="line">  interfaces: <span class="number">0</span>, fields: <span class="number">0</span>, methods: <span class="number">2</span>, attributes: <span class="number">1</span></span><br><span class="line">Constant pool:</span><br><span class="line">   #<span class="number">1</span> = Methodref          #<span class="number">2.</span>#<span class="number">3</span>          <span class="comment">// java/lang/Object.&quot;&lt;init&gt;&quot;:()V</span></span><br><span class="line">   #<span class="number">2</span> = Class              #<span class="number">4</span>             <span class="comment">// java/lang/Object</span></span><br><span class="line">   #<span class="number">3</span> = NameAndType        #<span class="number">5</span>:#<span class="number">6</span>          <span class="comment">// &quot;&lt;init&gt;&quot;:()V</span></span><br><span class="line">   #<span class="number">4</span> = Utf8               java/lang/Object</span><br><span class="line">   #<span class="number">5</span> = Utf8               &lt;init&gt;</span><br><span class="line">   #<span class="number">6</span> = Utf8               ()V</span><br><span class="line">   #<span class="number">7</span> = Fieldref           #<span class="number">8.</span>#<span class="number">9</span>          <span class="comment">// java/lang/System.out:Ljava/io/PrintStream;</span></span><br><span class="line">   #<span class="number">8</span> = Class              #<span class="number">10</span>            <span class="comment">// java/lang/System</span></span><br><span class="line">   #<span class="number">9</span> = NameAndType        #<span class="number">11</span>:#<span class="number">12</span>        <span class="comment">// out:Ljava/io/PrintStream;</span></span><br><span class="line">  #<span class="number">10</span> = Utf8               java/lang/System</span><br><span class="line">  #<span class="number">11</span> = Utf8               out</span><br><span class="line">  #<span class="number">12</span> = Utf8               Ljava/io/PrintStream;</span><br><span class="line">  #<span class="number">13</span> = String             #<span class="number">14</span>            <span class="comment">// hello world</span></span><br><span class="line">  #<span class="number">14</span> = Utf8               hello world</span><br><span class="line">  #<span class="number">15</span> = Methodref          #<span class="number">16.</span>#<span class="number">17</span>        <span class="comment">// java/io/PrintStream.println:(Ljava/lang/String;)V</span></span><br><span class="line">  #<span class="number">16</span> = Class              #<span class="number">18</span>            <span class="comment">// java/io/PrintStream</span></span><br><span class="line">  #<span class="number">17</span> = NameAndType        #<span class="number">19</span>:#<span class="number">20</span>        <span class="comment">// println:(Ljava/lang/String;)V</span></span><br><span class="line">  #<span class="number">18</span> = Utf8               java/io/PrintStream</span><br><span class="line">  #<span class="number">19</span> = Utf8               println</span><br><span class="line">  #<span class="number">20</span> = Utf8               (Ljava/lang/String;)V</span><br><span class="line">  #<span class="number">21</span> = Class              #<span class="number">22</span>            <span class="comment">// cn/wenhe9/yygh/hosp/test/HelloWorld</span></span><br><span class="line">  #<span class="number">22</span> = Utf8               cn/wenhe9/yygh/hosp/test/HelloWorld</span><br><span class="line">  #<span class="number">23</span> = Utf8               Code</span><br><span class="line">  #<span class="number">24</span> = Utf8               LineNumberTable</span><br><span class="line">  #<span class="number">25</span> = Utf8               main</span><br><span class="line">  #<span class="number">26</span> = Utf8               ([Ljava/lang/String;)V</span><br><span class="line">  #<span class="number">27</span> = Utf8               SourceFile</span><br><span class="line">  #<span class="number">28</span> = Utf8               HelloWorld.java</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">public</span> cn.wenhe9.yygh.hosp.test.HelloWorld();</span><br><span class="line">    descriptor: ()V</span><br><span class="line">    flags: (<span class="number">0x0001</span>) ACC_PUBLIC</span><br><span class="line">    Code:</span><br><span class="line">      stack=<span class="number">1</span>, locals=<span class="number">1</span>, args_size=<span class="number">1</span></span><br><span class="line">         <span class="number">0</span>: aload_0</span><br><span class="line">         <span class="number">1</span>: invokespecial #<span class="number">1</span>                  <span class="comment">// Method java/lang/Object.&quot;&lt;init&gt;&quot;:()V</span></span><br><span class="line">         <span class="number">4</span>: <span class="keyword">return</span></span><br><span class="line">      LineNumberTable:</span><br><span class="line">        line <span class="number">7</span>: <span class="number">0</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(java.lang.String[])</span>;</span><br><span class="line">    descriptor: ([Ljava/lang/String;)V</span><br><span class="line">    flags: (<span class="number">0x0009</span>) ACC_PUBLIC, ACC_STATIC</span><br><span class="line">    Code:</span><br><span class="line">      stack=<span class="number">2</span>, locals=<span class="number">1</span>, args_size=<span class="number">1</span></span><br><span class="line">         <span class="number">0</span>: getstatic     #<span class="number">7</span>                  <span class="comment">// Field java/lang/System.out:Ljava/io/PrintStream;</span></span><br><span class="line">         <span class="number">3</span>: ldc           #<span class="number">13</span>                 <span class="comment">// String hello world</span></span><br><span class="line">         <span class="number">5</span>: invokevirtual #<span class="number">15</span>                 <span class="comment">// Method java/io/PrintStream.println:(Ljava/lang/String;)V</span></span><br><span class="line">         <span class="number">8</span>: <span class="keyword">return</span></span><br><span class="line">      LineNumberTable:</span><br><span class="line">        line <span class="number">9</span>: <span class="number">0</span></span><br><span class="line">        line <span class="number">10</span>: <span class="number">8</span></span><br><span class="line">&#125;</span><br><span class="line">SourceFile: <span class="string">&quot;HelloWorld.java&quot;</span></span><br></pre></td></tr></table></figure></div>
<h3 id="stringtable"><a class="markdownIt-Anchor" href="#stringtable">#</a> StringTable</h3>
<p>常量池和串池的关系</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//StringTable[&quot;a&quot;, &quot;b&quot;, &quot;ab&quot;] hashtable结构，不能扩容</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloWorld</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">//常量池中的信息，都会被加载到运行时常量池中，这时 a b ab 都是常量值中的符号，还没有变为 java 字符串对象</span></span><br><span class="line">        <span class="comment">//ldc #2 会把 a 符号 变为 &quot;a&quot; 字符串对象</span></span><br><span class="line">        <span class="comment">//ldc #3 会把 b 符号 变为 &quot;b&quot; 字符串对象</span></span><br><span class="line">        <span class="comment">//ldc #4 会把 ab 符号 变为 &quot;ab&quot; 字符串对象</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">s1</span> <span class="operator">=</span> <span class="string">&quot;a&quot;</span>; <span class="comment">//懒惰的 当运行到时，先去串池找，没有把他放入串池，有就用串池的</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">s2</span> <span class="operator">=</span> <span class="string">&quot;b&quot;</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">s3</span> <span class="operator">=</span> <span class="string">&quot;ab&quot;</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">s4</span> <span class="operator">=</span> s1 + s2; <span class="comment">// new StringBuilder().append(&quot;a&quot;).append(&quot;b&quot;).toString() new String(&quot;ab&quot;)</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">s5</span> <span class="operator">=</span> <span class="string">&quot;a&quot;</span> + <span class="string">&quot;b&quot;</span>; <span class="comment">//javac在编译器的优化，&quot;a&quot;和&quot;b&quot;都是常量，拼接的结果是确定的，既然是确定的，那么在编译期间知道他的结果就是&quot;ab&quot;不会是其他的,不需要再像上面的方式进行拼接</span></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></div>
<h4 id="特性"><a class="markdownIt-Anchor" href="#特性">#</a> 特性</h4>
<ul>
<li>常量池中的字符串近是符号，第一次用到时才变为对象</li>
<li>利用串池的机制，来避免重复创建字符串对象</li>
<li>字符串变量拼接的原理是 StringBuilder (1.8)</li>
<li>字符串常量拼接的原理是编译器优化</li>
<li>可以使用 <code>intern</code>  方法，主动将串池中还没有的字符串对象放入串池</li>
</ul>
<blockquote>
<p>返回字符串对象的规范表示。一个字符串池，最初是空的，由 String 类私下维护。当调用 intern 方法时，如果池中已经包含一个等于该 String 对象的字符串，由 equals (Object) 方法确定，则返回池中的字符串。否则，将此 String 对象添加到池中并返回对该 String 对象的引用。</p>
</blockquote>
<p>在 1.6 中，常量池包括字符串常量池在永久代 perm 中，所以，当使用常量时是直接放在常量池中，当使用变量拼接时字符串时，他先经过创建 StringBuilder，调用构造方法，加载参数，调用 append 方法，最后调用 toString 方法，在 toString 方法中，通过 new String (value) 创建对象，是在堆中。当使用 intern 方法时，先看字符串常量池中是否有该字符串，如果有，就不放入，如果没有，就把这个字符串的值拷贝到字符串常量池中，在返回他的地址引用，所以此时有两个该字符串，一个是最开始的在堆中，一个因为拷贝在字符串常量池或者说是在永久代中，所以两个对象不相同</p>
<p>在 1.8 中，常量池在元空间中，而字符串常量池在堆中，并且当调用 intren 方法时，先看字符串常量池中是否有该字符串，如果有，就返回字符串常量池中的地址引用，如果没有，就把该字符串的地址存入字符串常量池并返回这个地址引用</p>
<p>在 1.6 中，如果要测试，字符串常量池在永久代中，可以 <code>-XX:MaxPermSize=10m</code>  设置永久代的大小，之后程序一直往字符串常量池中添加字符串，并占有字符串的引用</p>
<p>在 1.8 中，如果要测试，字符串常量池在堆中，除了要设置 <code>-Xmx10m</code>  设置堆内存的大小，还要设置 <code>-XX:-UseGCOverHeadLimit</code> ，这是这是垃圾回收的一个限制，当 98% 的时间花在了垃圾回收，但是只有 2% 的对空间被回收，会出现内存溢出异常异常 <code>+</code>  就是打开， <code>-</code>  就是关闭</p>
<h4 id="stringtable垃圾回收"><a class="markdownIt-Anchor" href="#stringtable垃圾回收">#</a> StringTable 垃圾回收</h4>
<p>StringTable 在堆内存中，也会触发垃圾回收</p>
<p>测试:</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.wenhe9.yygh.hosp.test;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> DuJinliang</span></span><br><span class="line"><span class="comment"> * 2022/2/21</span></span><br><span class="line"><span class="comment"> -Xmx10M -XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloWorld</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">100</span>; j++) &#123;</span><br><span class="line">                String.valueOf(j).intern();</span><br><span class="line">                i++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;<span class="keyword">catch</span> (Throwable e)&#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            System.out.println(i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div>
<ul>
<li><code>-Xmx10M -XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc</code>
<ul>
<li><code>-Xmx10M</code>  设置堆内存大小为 10M</li>
<li><code>-XX:+PrintStringTableStatistics</code>  打印字符串表的信息</li>
<li><code>-XX:+PrintGCDetails</code>  打印垃圾回收的信息</li>
<li><code>verbose:gc</code>  设置日志级别为 gc</li>
</ul>
</li>
</ul>
<h4 id="stringtable性能调优"><a class="markdownIt-Anchor" href="#stringtable性能调优">#</a> StringTable 性能调优</h4>
<ol>
<li>​	 <code>-XX:StringTableSize=1009</code>  设置串表的 hash 表的桶的大小尽可能的大</li>
<li>字符串对象为什么要入池？
<ul>
<li>防止重复的字符串对象占用内存</li>
</ul>
</li>
</ol>
<h3 id="直接内存"><a class="markdownIt-Anchor" href="#直接内存">#</a> 直接内存</h3>
<h4 id="定义-6"><a class="markdownIt-Anchor" href="#定义-6">#</a> 定义</h4>
<p>Direct Memory</p>
<ul>
<li>常见于 NIO 操作时，用于数据缓冲区</li>
<li>分配回收成本较高，单独写性能高</li>
<li>不受 JVM 内存回收管理</li>
</ul>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220226195015543.png"
                      alt="image-20220226195015543"
                ></p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220226195035365.png"
                      alt="image-20220226195035365"
                ></p>
<h4 id="分配和回收原理"><a class="markdownIt-Anchor" href="#分配和回收原理">#</a> 分配和回收原理</h4>
<ul>
<li>使用了 Unsafe 对象完成直接内存的分配和回收，并且回收需要主动调用 freeMemory 方法</li>
<li>ByteBuffer 的实现类内部，使用了 Cleaner（虚引用）来监测 ByteBuffer 对象，一旦 ByteBuffer 对象被垃圾回收，那么就会由 ReferenceHandler 线程通过 Cleaner 的 clean 方法调用 freeMemory 方法来释放内存</li>
</ul>
<h4 id="性能调优"><a class="markdownIt-Anchor" href="#性能调优">#</a> 性能调优</h4>
<ul>
<li>禁用显式回收对直接内存的影响
<ul>
<li><code>-XX:+DisableExplictGC</code>  禁用显式垃圾回收</li>
<li>如 <code>System.gc()</code>  显式的垃圾回收， Full GC 除了 新生代 老年代也会被回收</li>
</ul>
</li>
<li>可以使用 <code>Unsafe</code>  对象直接进行对内存的分配和回收，直接来管理这块直接内存</li>
</ul>
<h2 id="执行引擎"><a class="markdownIt-Anchor" href="#执行引擎">#</a> 执行引擎</h2>
<h3 id="垃圾回收"><a class="markdownIt-Anchor" href="#垃圾回收">#</a> 垃圾回收</h3>
<h4 id="如何判断对象可以回收"><a class="markdownIt-Anchor" href="#如何判断对象可以回收">#</a> 如何判断对象可以回收</h4>
<h5 id="引用计数法"><a class="markdownIt-Anchor" href="#引用计数法">#</a> 引用计数法</h5>
<p>只要一个对象被其他对象所引用，那么这个对象的引用加一，如果引用了两次那么引用计数加二，如果一个变量不再引用她了，那么他的引用计数减一，当他的引用计数变为 0 的时候，代表没有对象再引用他了，那么他就可以作为一个垃圾进行一个回收。</p>
<p><strong>弊端</strong> ： 循环引用</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220226203108552.png"
                      alt="image-20220226203108552"
                ></p>
<h5 id="可达性分析算法"><a class="markdownIt-Anchor" href="#可达性分析算法">#</a> 可达性分析算法</h5>
<p>首先确定一系列的根对象，所谓根对象，就是肯定不能被当成垃圾回收的对象，就称之为根对象，在垃圾回收之前，首先会对堆内存中的所有对象进行一遍扫描，看每一个对象是否被刚刚的根对象直接或者间接的引用，如果是，那么这个对象就不能被回收，反之，如果一个对象没有被根对象直接或者间接的引用，那么这个对象就可以作为垃圾将来被回收</p>
<p><code>jmap -dump:format=b,live,file=文件名称 进程号</code> 抓取当前 jvm 堆的状态 b 是二进制，live 是获取当前存活的对象，会触发一次垃圾回收</p>
<p>使用 mat（Memory Analyze Tool）工具分析堆</p>
<h5 id="四中引用"><a class="markdownIt-Anchor" href="#四中引用">#</a> 四中引用</h5>
<ol>
<li>强引用
<ul>
<li><code>new</code>  了一个对象，把这个对象通过 <code>=</code>  赋值运算符赋值给了一个变量，那么这个变量就强引用了这个对象</li>
<li>特点
<ul>
<li>只要沿着 GC Root 的引用链能够找到他，就不会被垃圾回收</li>
</ul>
</li>
<li>总结
<ul>
<li>只有所有 GC Roots 对象都不通过【强引用】引用该对象，该对象才能被垃圾回收</li>
</ul>
</li>
</ul>
</li>
<li>软引用 (SoftReference)
<ul>
<li>当垃圾回收时，并且内存不够时，会把软引用的对象回收掉，前提是没有强引用引用他</li>
<li>总结
<ul>
<li>仅有软引用引用该对象时，在垃圾回收后，内存仍不足会再次发出垃圾回收，回收软引用的对象</li>
<li>可以配合引用队列来释放软引用自身</li>
</ul>
</li>
</ul>
</li>
<li>弱引用（WeakReference）
<ul>
<li>只要发生了垃圾回收，不管内存是否充足，都会把若引用的对象回收掉，前提是没有强引用引用他</li>
<li>总结
<ul>
<li>仅有弱引用引用该对象时，在垃圾回收时，无论内存是否充足，都会回收弱引用对象</li>
<li>可以配合引用队列来释放弱引用自身</li>
</ul>
</li>
</ul>
</li>
<li>虚引用（PhantomReference）
<ul>
<li>直接内存</li>
<li>在虚引用引用的对象在垃圾回收时，虚引用自己就会放入引用队列，从而间接的用一个线程调用虚引用的 unsafe 的 freeMemory 方法去释放那块直接内存</li>
<li>总结
<ul>
<li>必须配合引用队列使用，主要配合 ByteBuffer 使用，被引用对象回收时，会将虚引用入队，由 Reference Handler 线程调用虚引用相关方法释放直接内存</li>
</ul>
</li>
</ul>
</li>
<li>终接器引用（finalReference）
<ul>
<li>当没有强引用引用对象时，对象会由虚拟机创建对应的终结器引用，当这个对象被垃圾回收时， 把终结器引用加入引用队列，由一个优先级很低的 finalizeHandler 线程，在某些时机查看引用队列中是否有终结器引用，如果有，就根据终结器引用找到刚刚要垃圾回收的对象，并且调用他的 finalize 方法，等调用完了，下一次垃圾回收时，就会把这个对象回收掉</li>
<li>总结
<ul>
<li>无需手动编码，但其内部配合引用队列使用，在垃圾回收时，终结器引用入队（被引用对象暂时没有被回收），再由 Finalizer 线程通过终结器引用找到被引用对象并调用他的 finalize 方法，第二次 GC 时才能回收被引用对象</li>
</ul>
</li>
</ul>
</li>
</ol>
<blockquote>
<p>当软引用和弱引用没有直接的强引用所引用，那么当垃圾回收发生时，他们都有可能被回收掉</p>
<p>如果软引用、若引用，当引用的对象被回收掉了以后，那么软引用自身也是一个对象，他如果在创建时给他分配了一个引用对队列，他所引用的对象被回收时，他就会进入引用队列，因为软引用、弱引用自身也占了一定的内存，如果要对他两所占用的内存做进一步的释放，可以通过引用队列找到他两，对他作进一步的处理，比如他两还可能被强引用所引用，所以可以在引用队列中依次遍历，把他们释放掉</p>
<p>软、弱队列既可以配合引用队列使用也可以不配合引用队列使用</p>
<p>虚引用和终结器引用必须配合引用队列使用</p>
</blockquote>
<ul>
<li>
<p>软引用</p>
<ul>
<li>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><figcaption data-lang="java"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">HelloWorld</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span>  <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token comment">// 内存空间比较有限，一些不重要的资源用软引用去关联，当空间紧张时就可以回收掉</span></pre></td></tr><tr><td data-num="5"></td><td><pre></pre></td></tr><tr><td data-num="6"></td><td><pre>        <span class="token comment">// 引用队列</span></pre></td></tr><tr><td data-num="7"></td><td><pre>        <span class="token class-name">ReferenceQueue</span><span class="token operator">&lt;</span><span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">></span> queue <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ReferenceQueue</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="8"></td><td><pre></pre></td></tr><tr><td data-num="9"></td><td><pre>        <span class="token class-name">List</span><span class="token operator">&lt;</span><span class="token class-name">SoftReference</span><span class="token operator">&lt;</span><span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">>></span> list <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">5</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="11"></td><td><pre>            <span class="token comment">// 关联了引用队列，当软引用所关联的 byte [] 被回收时，软引用自己会加入到 queue 中去</span></pre></td></tr><tr><td data-num="12"></td><td><pre>            <span class="token class-name">SoftReference</span><span class="token operator">&lt;</span><span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">></span> ref <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SoftReference</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token number">4</span> <span class="token operator">*</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span><span class="token punctuation">]</span><span class="token punctuation">,</span> queue<span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="13"></td><td><pre>            <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>ref<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="14"></td><td><pre>            list<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>ref<span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="15"></td><td><pre>            <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>list<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="16"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="17"></td><td><pre></pre></td></tr><tr><td data-num="18"></td><td><pre>        <span class="token comment">// 从队列中获取无用的 软引用 对象，并移除</span></pre></td></tr><tr><td data-num="19"></td><td><pre>        <span class="token class-name">Reference</span><span class="token operator">&lt;</span><span class="token operator">?</span> <span class="token keyword">extends</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">></span> poll <span class="token operator">=</span> queue<span class="token punctuation">.</span><span class="token function">poll</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="20"></td><td><pre>        <span class="token keyword">while</span> <span class="token punctuation">(</span>poll <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="21"></td><td><pre>            list<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>poll<span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="22"></td><td><pre>            poll <span class="token operator">=</span> queue<span class="token punctuation">.</span><span class="token function">poll</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="23"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="24"></td><td><pre></pre></td></tr><tr><td data-num="25"></td><td><pre>        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">SoftReference</span><span class="token operator">&lt;</span><span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">></span> softReference <span class="token operator">:</span> list<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="26"></td><td><pre>            <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>softReference<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="27"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="28"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="29"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="30"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">5</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
</ul>
<h4 id="垃圾回收算法"><a class="markdownIt-Anchor" href="#垃圾回收算法">#</a> 垃圾回收算法</h4>
<h5 id="标记清除"><a class="markdownIt-Anchor" href="#标记清除">#</a> 标记清除</h5>
<p>分两个阶段，第一个阶段，根据可达性算法标记不再被使用的对象，第二个阶段，对被标记的对象进行回收，回收只是将垃圾对象的内存起始结束地址做一个标记，存储到空闲分区表中</p>
<ul>
<li>优点
<ul>
<li>速度快，清楚操作只需要把垃圾对象的内存起始结束地址做一个记录，不需要做更多的额外处理，所以他的清楚速度、整个垃圾回收的速度是比较快的</li>
</ul>
</li>
<li>缺点
<ul>
<li>容易产生内存碎片，在清除以后不会再对空闲的内存空间作进一步的整理工作，如果分配了一个较大的对象，每个空闲的内存空间都不足以满足需要，但是若干个空闲的总内存空间是足够的，但是因为空间不连续（内存碎片），造成新对象仍然不能有一个有效的内存来使用，还是会造成内存溢出</li>
</ul>
</li>
</ul>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220228125439542.png"
                      alt="image-20220228125439542"
                ></p>
<h5 id="标记整理"><a class="markdownIt-Anchor" href="#标记整理">#</a> 标记整理</h5>
<p>分两个阶段，第一个阶段标记，先对对象进行标记，看哪些对象是垃圾，第二个阶段整理，在清除垃圾的过程中，他会把可用的对象向前移动，使内存更为紧凑</p>
<ul>
<li>优点
<ul>
<li>没有内存碎片</li>
</ul>
</li>
<li>缺点
<ul>
<li>由于整理牵扯到对象的移动，效率变得较低</li>
</ul>
</li>
</ul>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220228125709844.png"
                      alt="image-20220228125709844"
                ></p>
<h5 id="复制"><a class="markdownIt-Anchor" href="#复制">#</a> 复制</h5>
<p>将内存分为两个区域，一个 FROM 区，一个 TO 区两个阶段，其中 TO 区始终空闲着，一个对象都没有，找到哪些被引用的对象，将他们标记为垃圾，从 FROM 区上存活的对象复制到 TO 区，复制的过程中就会完成碎片的整理，等复制完成 FROM 区全是垃圾的，把 FROM 区清空，交换 FROM 和 TO 的位置，TO 总是空闲的一块分区</p>
<ul>
<li>优点
<ul>
<li>不会产生碎片</li>
</ul>
</li>
<li>缺点
<ul>
<li>复制算法会占用双倍的内存空间</li>
</ul>
</li>
</ul>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220228131040558.png"
                      alt="image-20220228131040558"
                ></p>
<p>虚拟机不会采用单独的一种算法，结合前面的三种算法协同工作，具体实现是虚拟机中分代的垃圾回收机制，他把堆内存大的区域分成两块，一块是新生代，一块是老年代，而在新生代里又进一步划分成三个小的区域：伊甸园、幸存区 From、幸存区 To</p>
<blockquote>
<p>为什么要这样的区域划分？</p>
<p>主要是因为 Java 中有的对象需要长时间使用，长时间使用的对象就放在老年代中，而用完就要丢弃的对象，就放在新生代中，这样就可以针对对象的生命周期的不同特点进行不同的垃圾回收策略，老年代的垃圾回收很久才发生一次，新生代的垃圾回收就发生的比较频繁，新生代处理的都是朝生夕死的对象，而老年代处理的都是更有价值、长时间存活的对象，这样不同的区域采用不同的算法可以更有效得对垃圾回收做一个管理</p>
</blockquote>
<ul>
<li>当创建一个新的对象时，那这个新的对象，就会默认采用伊甸园的一片空间，</li>
<li>当新的对象不断的创建，伊甸园逐渐被占满了，当再有一个对象被创建时，伊甸园的空间已经不够了、容纳不下了，这时候就会触发一次垃圾回收，</li>
<li>新生代的垃圾回收称作 <code>Minor GC</code> ，引发  <code>Stop the World</code>  暂停其他的用户线程，由垃圾回收线程完成垃圾回收的动作，当垃圾回收动作做完用户线程才能继续运行，</li>
<li>采用复制算法，把存活的对象复制到幸存区 To 中，复制过来以后，会将幸存对象的寿命加一，刚开始寿命是 0，经历了一次垃圾回收还不死，那么寿命加一，交换幸存区 From 和幸存区 To 的位置（实际变得不是物理地址，而是地址引用），</li>
<li>继续向伊甸园分配新的对象，刚才放不下的对象就放进去了，</li>
<li>又经过了一段时间，伊甸园又满了，又触发一次垃圾回收，这一次除了要把伊甸园中存活的对象找到，还要找到幸存区 From 中存活的对象，把所有存活的对象放到幸存区 To 中，寿命加一，交换 From 和 To，那么新对象又可以放入伊甸园</li>
<li>当幸存区 From 中存活的对象经历了 15 次垃圾回收还活着（寿命超过一定的阈值，最大寿命是 15，4bit，在不同的垃圾回收器阈值不同），说明该对象价值比较高，就把他晋升到老年代，因为老年代的垃圾回收频率比较低，不会轻易地回收掉，那么价值较高的对象，就把他晋升到老年代中。</li>
<li>如果老年代的内存空间不足时，先尝试 <code>Minor GC</code>  看能否把新生代的空间释放一部分分配对象，结果发现 <code>Minor GC</code>  以后空间还不足，就会触发 <code>Full GC</code> ，从新生到老年一个全面的清理，相对于 <code>Minor GC</code>  的 STW 的时间要更长，老年代采用的回收算法和新生代是不一样的，老年代的对象比较多，采用的算法是标记清除或者标记整理，后者较慢，老年代的对象都不是那么容易被当成垃圾回收，所以回收效率、时间更低，STW 的时间也会更长，如果 <code>FULL GC</code>  后，空间足够，则皆大欢喜，否则，分配失败，抛出内存溢出异常</li>
</ul>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220228135050354.png"
                      alt="image-20220228135050354"
                ></p>
<blockquote>
<p>为什么垃圾回收的时候会把其他用户正在执行的线程暂停掉？</p>
<p>因为垃圾回收牵扯到对象的复制，对象的地址会发成改变，如果多个线程同时运行，就会造成混乱，对象都移动了，其他线程在根据原来的地址就找不到了</p>
</blockquote>
<h3 id="相关vm参数"><a class="markdownIt-Anchor" href="#相关vm参数">#</a> 相关 VM 参数</h3>
<table>
<thead>
<tr>
<th>含义</th>
<th>参数</th>
</tr>
</thead>
<tbody>
<tr>
<td>堆初始大小</td>
<td>-Xms</td>
</tr>
<tr>
<td>堆最大大小</td>
<td>-Xmx 或 -XX:MaxHeapSize=size</td>
</tr>
<tr>
<td>新生代大小</td>
<td>-Xmn 或 (-XX:NewSize=size + -XX:MaxNewSize=size)</td>
</tr>
<tr>
<td>幸存区比例（动态）</td>
<td>-XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy</td>
</tr>
<tr>
<td>幸存区比例</td>
<td>-XX:SuvivorRatio=ratio</td>
</tr>
<tr>
<td>晋升阈值</td>
<td>-XX:MaxTenuringThreshold=threshold</td>
</tr>
<tr>
<td>晋升详情</td>
<td>-XX:+PrintTenuringDistribution</td>
</tr>
<tr>
<td>GC 详情</td>
<td>-XX:+PringGCDetails -verbose:gc</td>
</tr>
<tr>
<td>FullGC 前 MinorGC</td>
<td>-XX:+ScavengeBeforeFullGC</td>
</tr>
</tbody>
</table>
<blockquote>
<p>当子线程发生 OOM 错误时，会将子线程的全部资源释放掉，故而不影响到主线程</p>
</blockquote>
<h3 id="垃圾回收器"><a class="markdownIt-Anchor" href="#垃圾回收器">#</a> 垃圾回收器</h3>
<ol>
<li>串行
<ul>
<li>单线程</li>
<li>堆内存较小，适合个人电脑</li>
</ul>
</li>
<li>吞吐量优先
<ul>
<li>多线程</li>
<li>堆内存较大，多核 CPU</li>
<li>让单位时间内，STW 的时间最短</li>
</ul>
</li>
<li>响应时间有效
<ul>
<li>多线程</li>
<li>堆内存较大，多核 CPU</li>
<li>尽可能让 STW 的单次时间最短</li>
</ul>
</li>
</ol>
<h4 id="串行"><a class="markdownIt-Anchor" href="#串行">#</a> 串行</h4>
<p><code>-XX:+UseSerialGC = Serial + SerialOld</code></p>
<p>Serial 工作在新生代，使用复制算法</p>
<p>SerialOld 工作在老年代，采用标记加整理算法</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220228151826294.png"
                      alt="image-20220228151826294"
                ></p>
<h4 id="吞吐量优先"><a class="markdownIt-Anchor" href="#吞吐量优先">#</a> 吞吐量优先</h4>
<p>1.8 默认使用并行的垃圾回收器 Parallel  Parallel（并行）</p>
<p>-XX:+UseParallelGC 新生代的垃圾回收器 复制算法 开启其中一个会连带着开启另一个</p>
<p>-XX:+UseParallelOldGC 老年代的垃圾回收器 标记 + 整理算法</p>
<p>-XX:+UseAdaptiveSizePolicy 动态调整伊甸园和幸存区的比例包括整个堆的大小以及晋升阈值的大小</p>
<p>-XX:GCTimeRatio=ratio 一般是 19 0.05 一百分钟内允许五分钟的垃圾回收时间 调整垃圾回收的时间占总时间的占比 1/1+ratio 如果达不到目标，Parallel 回收期就会调整堆的大小来达到这个目标</p>
<p>-XX:MaxGCPauseMillis=ms 最大暂停毫秒数 默认 200 毫秒 与上一个冲突</p>
<p>-XX:ParallelGCThreads=n</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220228152400678.png"
                      alt="image-20220228152400678"
                ></p>
<h4 id="响应时间优先"><a class="markdownIt-Anchor" href="#响应时间优先">#</a> 响应时间优先</h4>
<p>concurrent 垃圾回收器工作的同时，其他的用户线程，也能同时进行，也就是用户线程和垃圾回收线程是并发执行，而 parallel 是多个垃圾回收器并行运行，在此期间，不允许用户工作线程继续运行，stop the world</p>
<p>-XX:+UseConcMarkSweepGC  基于标记清除算法的垃圾回收器并且是并发的 concurrent (并发) 老年代 有时候会发生并发失败的问题，就会采取补救的措施，退化到 SerialOld 单线程的垃圾回收器</p>
<p>-XX:+UseParNewGC 	复制算法 新生代</p>
<p>-XX:ParallelGCThreads=n ~ -XX:ConcThreads=threads n/4</p>
<p>-XX:CMSInitiatingOccupancyFraction=percent 执行 CMS 的内存占比 比如 80 当老年代的内存占用达到 80% 时就执行一次垃圾回收，这样可以预留一些空间给浮动垃圾来用。默认值是 65%</p>
<p>-XX:+CMSScavengeBeforeRemark 在重新标记之前，对新生代进行一次垃圾回收，减轻重新标记的压力</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220228160055673.png"
                      alt="image-20220228160055673"
                ></p>
<p>因为是基于标记和清除算法，可以可能产生比较多的内存碎片，这样的话，就会造成，将来 Minor GC 不足，结果老年代过多，GC 也不足，就会造成并发失败，CMS 的垃圾回收器就不能正常操作，这个时候，垃圾回收器就会退化为 SerialOld 串行化的垃圾回收，做一些整理，碎片变少了，才能继续操作</p>
<h4 id="g1"><a class="markdownIt-Anchor" href="#g1">#</a> G1</h4>
<p>定义：Garbage First</p>
<p>使用场景</p>
<ul>
<li>同时注重吞吐量（Throught）和低延迟（Low latency），默认的暂停目标的是 200ms</li>
<li>超大堆内存，会将堆划分为多个大小相等的 Region</li>
<li>整体上是标记 + 整理算法，两个区域之间是复制算法</li>
</ul>
<p>相关 JVM 参数</p>
<p>-XX:+UseG1GC</p>
<p>-XX:G1HeapRegionSize=size</p>
<p>-XX:MaxGCPauseMillis=time</p>
<h5 id="1垃圾回收阶段"><a class="markdownIt-Anchor" href="#1垃圾回收阶段">#</a> 1) 垃圾回收阶段</h5>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220301083352858.png"
                      alt="image-20220301083352858"
                ></p>
<h5 id="2young-collection"><a class="markdownIt-Anchor" href="#2young-collection">#</a> 2)Young Collection</h5>
<ul>
<li>会 STW</li>
</ul>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220301083706505.png"
                      alt="image-20220301083706505"
                ></p>
<p>垃圾回收时，会把幸存对象复制进幸存区</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220301083804132.png"
                      alt="image-20220301083804132"
                ></p>
<p>当幸存区内存不足时，会再次触发垃圾回收，会将寿命达到阈值的对象放进老年代，寿命不够的会复制进另一个幸存区</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220301084022984.png"
                      alt="image-20220301084022984"
                ></p>
<h5 id="3young-collection-cm"><a class="markdownIt-Anchor" href="#3young-collection-cm">#</a> 3)Young Collection + CM</h5>
<ul>
<li>在 Young GC 时会进行 GC Roots 的初始标记</li>
<li>老年代占用比例达到阈值时，进行并发标记（不会 STW），由下面的 JVM 参数决定
<ul>
<li>``-XX:InitiatingHeapOccupancyPercent=percent （默认 45%）</li>
</ul>
</li>
</ul>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220301084507087.png"
                      alt="image-20220301084507087"
                ></p>
<h5 id="4mixed-collection"><a class="markdownIt-Anchor" href="#4mixed-collection">#</a> 4)Mixed Collection</h5>
<p>会对 E、S、O 进行全面的垃圾回收</p>
<ul>
<li>最终标记（Remark）会 STW</li>
<li>拷贝存活（Evacuation）会 STW</li>
</ul>
<p>会将新生代存活的对象复制进幸存区，幸存区达到寿命阈值的会进入老年代，幸存区寿命没有达到阈值的，复制进入另一个幸存区，若时间足够，即是否达到 <code>-XX:MaxGCPauseMillis=ms</code> ，若没有达到，则全部老年代的幸存对象进行复制进新的老年代中，否则，只选取部分回收价值最高的老年代进行回收和复制，一方面是为了保存存货对象，另一方面是为了减少内存碎片</p>
<p>STW 是因为并发标记时，用户线程还在运行，可能会改变一些对象的引用或者产生新的垃圾，对并发标记的最终结果产生影响，所以在最终标记时需要 STW</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220301085135007.png"
                      alt="image-20220301085135007"
                ></p>
<h5 id="5full-gc"><a class="markdownIt-Anchor" href="#5full-gc">#</a> 5)Full GC</h5>
<ul>
<li>SerialGC
<ul>
<li>新生代内存不足时发生的垃圾收集 - minor gc</li>
<li>老年代内存不足时发生的垃圾收集 - full gc</li>
</ul>
</li>
<li>ParallelGC
<ul>
<li>新生代内存不足时发生的垃圾收集 - minor gc</li>
<li>老年代内存不足时发生的垃圾收集 - full gc</li>
</ul>
</li>
<li>CMS
<ul>
<li>新生代内存不足时发生的垃圾收集 - minor gc</li>
<li>老年代内存不足时
<ul>
<li>如果并发收集没有失败（也就是垃圾回收后，内存足够时），那么就是并发收集</li>
<li>如果并发收集失败（也就是垃圾回收后，内存碎片严重，内存不足时），那么 CMS 回收器会退化成 Serial GC 使用标记整理算法，对内存碎片进行整理</li>
</ul>
</li>
</ul>
</li>
<li>G1
<ul>
<li>新生代内存不足时发生的垃圾收集 - minor gc</li>
<li>老年代内存不足时
<ul>
<li>如果垃圾回收的效率大于用户线程产生垃圾的效率，那么使用的是并发收集</li>
<li>如果垃圾回收的效率小于用户线程产生垃圾的效率，那么 G1 回收器退化为 Serial GC 使用标记 - 整理算法对内存碎片进行整理</li>
</ul>
</li>
</ul>
</li>
</ul>
<h5 id="6young-collection跨代引用"><a class="markdownIt-Anchor" href="#6young-collection跨代引用">#</a> 6) Young Collection 跨代引用</h5>
<ul>
<li>新生代回收的跨代引用（老年代引用新生代）问题</li>
</ul>
<p>在垃圾回收时，会将存活对象复制进幸存区，但是有一些新生代的根对象是来自老年代的，老年代中存活的对象对比较多，如果要遍历整个老年代去找这个根对象，效率是非常低的，因此采用卡表的技术 Card Table，把老年代的区域再进行一次细分，分成一个个的卡，每一个卡是 512k，如果老年代其中有一个对象引用了新生代的对象，就把这个卡标记为脏卡， 这样的话，就不用去找整个老年代，只需要去找脏卡的区域即可，减少搜索范围，提高扫描根对象的效率</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220301092711562.png"
                      alt="image-20220301092711562"
                ></p>
<ul>
<li>卡表与 Remember Set
<ul>
<li>在新生代这边有一个 Remember Set 记录从外部对新生代的引用，即脏卡</li>
<li>根据 Remember Set 知道有哪些脏卡，再到脏卡去遍历 GC Roots，减少了 GC Roots 的遍历时间</li>
</ul>
</li>
<li>在引用变更时，通过写屏障，异步的线程去完成脏卡的更新操作
<ul>
<li><code>post-write barrier + dirty card queue</code></li>
</ul>
</li>
<li><code>concurrent refinement threads</code>  更新 Remember Set</li>
</ul>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220301133805597.png"
                      alt="image-20220301133805597"
                ></p>
<h5 id="7remark"><a class="markdownIt-Anchor" href="#7remark">#</a> 7)Remark</h5>
<ul>
<li><code>pre-write barrier + satb_mark_queue</code></li>
<li>在并发标记的过程中，对已经标记的对象会加上一个写屏障，当在并发标记之后，用户线程更改了这个对象的引用，就会触发写屏障，将这个对象放入 <code>satb_mark_queue</code>  队列，当重新标记时，会对这个队列的对象进行重行检测</li>
</ul>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220301140303919.png"
                      alt="image-20220301140303919"
                ></p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220301140328043.png"
                      alt="image-20220301140328043"
                ></p>
<h5 id="8jdk8u20字符串去重"><a class="markdownIt-Anchor" href="#8jdk8u20字符串去重">#</a> 8) JDK8u20 字符串去重</h5>
<ul>
<li>优点：节省大量内存</li>
<li>缺点：略微多占用了 cpu 时间，新生代回收时间略微增加</li>
</ul>
<p>-XX:+UseStringDeduplication</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">s1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(<span class="string">&quot;abc&quot;</span>); <span class="comment">//char[]&#123;&quot;a&quot;, &quot;b&quot;, &quot;c&quot;&#125;</span></span><br><span class="line"><span class="type">String</span> <span class="variable">s1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(<span class="string">&quot;abc&quot;</span>); <span class="comment">//char[]&#123;&quot;a&quot;, &quot;b&quot;, &quot;c&quot;&#125;</span></span><br></pre></td></tr></table></figure></div>
<ul>
<li>将所有新分配的字符串放入一个队列</li>
<li>当新生代回收时，G1 并发检查是否有字符串重复</li>
<li>如果他们的值一样，让他们引用同一个 char []</li>
<li>注意，与 <code>String.intern()</code>  不一样
<ul>
<li><code>String.intern()</code>  关注的是字符串对象</li>
<li>而字符串去重关注的是 char []</li>
<li>在 JVM 内部，使用了不同的字符串表</li>
</ul>
</li>
</ul>
<blockquote>
<p>如果他们的值一样，让他们引用同一个 char []，那么两个字符串对象 == 是否为 true?</p>
</blockquote>
<ul>
<li>
<p>还是为 false，但是这里又说指向了同一个 char [] 是什么意思呢？</p>
</li>
<li>
<p>观察 String 源码，我们发现，String 对象的值是采用的 char [] 类型的 value 属性，这里所说的指向了同一个 char []，是指两个不同的 String 对象的 value 属性指向了同一个 char []</p>
</li>
<li>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><figcaption data-lang="java"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">class</span> <span class="token class-name">String</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">implements</span> <span class="token class-name"><span class="token namespace">java<span class="token punctuation">.</span>io<span class="token punctuation">.</span></span>Serializable</span><span class="token punctuation">,</span> <span class="token class-name">Comparable</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span></span><span class="token punctuation">,</span> <span class="token class-name">CharSequence</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token comment">/** The value is used for character storage. */</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">char</span> value<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">7</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
<p>执行 javac -parameters -d .HelloWorld.java</p>
<p>编译为 HelloWorld.class 后是这个样子</p>
<div class="highlight-container" data-rel="Shell"><figure class="iseeu highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">od -t xC HelloWorld.class</span><br><span class="line">0000000 ca fe ba be 00 00 00 34 00 1f 0a 00 06 00 11 09</span><br><span class="line">0000020 00 12 00 13 08 00 14 0a 00 15 00 16 07 00 17 07</span><br><span class="line">0000040 00 18 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29</span><br><span class="line">0000060 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e</span><br><span class="line">0000100 75 6d 62 65 72 54 61 62 6c 65 01 00 04 6d 61 69</span><br><span class="line">0000120 6e 01 00 16 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67</span><br><span class="line">0000140 2f 53 74 72 69 6e 67 3b 29 56 01 00 10 4d 65 74</span><br><span class="line">0000160 68 6f 64 50 61 72 61 6d 65 74 65 72 73 01 00 04</span><br><span class="line">0000200 61 72 67 73 01 00 0a 53 6f 75 72 63 65 46 69 6c</span><br><span class="line">0000220 65 01 00 0f 48 65 6c 6c 6f 57 6f 72 6c 64 2e 6a</span><br><span class="line">0000240 61 76 61 0c 00 07 00 08 07 00 19 0c 00 1a 00 1b</span><br><span class="line">0000260 01 00 0b 68 65 6c 6c 6f 20 77 6f 72 6c 64 07 00</span><br><span class="line">0000300 1c 0c 00 1d 00 1e 01 00 19 63 6e 2f 77 65 6e 68</span><br><span class="line">0000320 65 39 2f 74 65 78 74 2f 48 65 6c 6c 6f 57 6f 72</span><br><span class="line">0000340 6c 64 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f</span><br><span class="line">0000360 62 6a 65 63 74 01 00 10 6a 61 76 61 2f 6c 61 6e</span><br><span class="line">0000400 67 2f 53 79 73 74 65 6d 01 00 03 6f 75 74 01 00</span><br><span class="line">0000420 15 4c 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53</span><br><span class="line">0000440 74 72 65 61 6d 3b 01 00 13 6a 61 76 61 2f 69 6f</span><br><span class="line">0000460 2f 50 72 69 6e 74 53 74 72 65 61 6d 01 00 07 70</span><br><span class="line">0000500 72 69 6e 74 6c 6e 01 00 15 28 4c 6a 61 76 61 2f</span><br><span class="line">0000520 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 00 21</span><br><span class="line">0000540 00 05 00 06 00 00 00 00 00 02 00 01 00 07 00 08</span><br><span class="line">0000560 00 01 00 09 00 00 00 1d 00 01 00 01 00 00 00 05</span><br><span class="line">0000600 2a b7 00 01 b1 00 00 00 01 00 0a 00 00 00 06 00</span><br><span class="line">0000620 01 00 00 00 07 00 09 00 0b 00 0c 00 02 00 09 00</span><br><span class="line">0000640 00 00 25 00 02 00 01 00 00 00 09 b2 00 02 12 03</span><br><span class="line">0000660 b6 00 04 b1 00 00 00 01 00 0a 00 00 00 0a 00 02</span><br><span class="line">0000700 00 00 00 09 00 08 00 0a 00 0d 00 00 00 05 01 00</span><br><span class="line">0000720 0e 00 00 00 01 00 0f 00 00 00 02 00 10</span><br><span class="line">0000735</span><br></pre></td></tr></table></figure></div>
<p>根据 JVM 规范，类文件结构如下</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220304104332861.png"
                      alt="image-20220304104332861"
                ></p>
<h4 id="魔数"><a class="markdownIt-Anchor" href="#魔数">#</a> 魔数</h4>
<p>0-3 字节，表示他是否是【class】类型的文件</p>
<p>0000000 <span style="background-color : red">ca fe ba be</span> 00 00 00 34 00 23 0a 00 06 00 15 09</p>
<h4 id="版本"><a class="markdownIt-Anchor" href="#版本">#</a> 版本</h4>
<p>4-7 字节，表示类的版本 00 34 (52) 表示 Java8</p>
<p>十六进制，34H 等于十进制的 52 ，52 就是 Java8 53 是 Java9</p>
<p>0000000 ca fe ba be <span style="background-color : red">00 00 00 34</span>  00 23 0a 00 06 00 15 09</p>
<h3 id="类加载阶段"><a class="markdownIt-Anchor" href="#类加载阶段">#</a> 类加载阶段</h3>
<h4 id="类加载"><a class="markdownIt-Anchor" href="#类加载">#</a> 类加载</h4>
<ul>
<li>将类的字节码载入方法区中，内部采用 C++ 的 instanceKlass 的描述 java 类，他的重要 field 有：
<ul>
<li>_java_mirror 即 java 的类镜像，例如对 String 来说，就是 String.class，作用是把 klass 暴露给 java 使用</li>
<li>_super 即父类</li>
<li>_fields 即成员变量</li>
<li>_methods 即方法</li>
<li>_constants 即常量池</li>
<li>_class_loader 即类加载器</li>
<li>_vtable 虚方法表</li>
<li>_itable 接口方法表</li>
</ul>
</li>
<li>如果这个类还有父类没有加载，先加载父类</li>
<li>加载和链接可能是交替运行的</li>
</ul>
<blockquote>
<p>注意</p>
<p>instanceKlass 这样的【元数据】是存储在方法区（1.8 后的元空间内），但_java_mirror 是存储在堆中的</p>
<p>可以通过 hsdb 查看</p>
</blockquote>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220305152956911.png"
                      alt="image-20220305152956911"
                ></p>
<h4 id="连接"><a class="markdownIt-Anchor" href="#连接">#</a> 连接</h4>
<ol>
<li>验证：验证类是否符合 JVM 规范，安全性检查
<ul>
<li>用 UE 等支持二进制的编辑器修改 HelloWorld.class 的魔数，在控制台运行</li>
</ul>
</li>
<li>准备：为 static 变量分配空间，设置默认值
<ul>
<li>static 变量在 JDK7 之前存储于 instanceKlass 末尾，从 JDK7 开始，存储于_java_mirror 末尾</li>
<li>static 变量分配空间和赋值是两个步骤，分配空间在准备阶段完成，赋值在初始化阶段完成</li>
<li>如果 static 变量是 final 的基本类型，那么编译阶段值就确定了，赋值在准备阶段完成</li>
<li>如果 static 变量是 final 的，但属于引用类型，那么赋值也会在初始化阶段完成</li>
</ul>
</li>
</ol>
<h4 id="解析"><a class="markdownIt-Anchor" href="#解析">#</a> 解析</h4>
<ul>
<li>将常量池中的符号引用解析为直接引用</li>
</ul>
<h4 id="初始化"><a class="markdownIt-Anchor" href="#初始化">#</a> 初始化</h4>
<p><cinit>() V 方法</p>
<p>初始化即调用<cinit>() V 方法，虚拟机会保证这个类的【构造方法】的线程安全</p>
<h5 id="发生的时机"><a class="markdownIt-Anchor" href="#发生的时机">#</a> 发生的时机</h5>
<p>概括的说，类初始化是【懒惰的】</p>
<ul>
<li>main 方法所在的类，总会被首先初始化</li>
<li>首次访问这个类的静态变量或静态方法时</li>
<li>子类初始化，如果父类还没初始化，会引发</li>
<li>子类访问父类的静态变量，只会触发父类的初始化</li>
<li>Class.forName</li>
<li>new 会导致初始化</li>
</ul>
<p>不会导致类初始化的情况</p>
<ul>
<li>访问类的 static final 静态常量（基本类型和字符串）不会触发初始化</li>
<li>类对象.class 不会触发初始化</li>
<li>创建该类的数组不会触发初始化</li>
<li>类加载器的 loadClass 方法</li>
<li>Class.forName 的参数 2 为 false 时</li>
</ul>
<h3 id="类加载器"><a class="markdownIt-Anchor" href="#类加载器">#</a> 类加载器</h3>
<p>以 JDK8 为例</p>
<table>
<thead>
<tr>
<th>名称</th>
<th>加载哪的类</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bootstrap ClassLoader（C++）</td>
<td>JAVA_HOME/jre/lib</td>
<td>无法直接访问</td>
</tr>
<tr>
<td>Extension ClassLoader</td>
<td>JAVA_HOME/jre/lib/ext</td>
<td>上级为 Bootstrap，显示为 null</td>
</tr>
<tr>
<td>Application ClassLoader</td>
<td>classpath</td>
<td>上级为 Extension</td>
</tr>
<tr>
<td>自定义类加载器</td>
<td>自定义</td>
<td>上极为 Application</td>
</tr>
</tbody>
</table>
<h5 id="启动类加载器"><a class="markdownIt-Anchor" href="#启动类加载器">#</a> 启动类加载器</h5>
<div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">java -Xbootclasspath/a:. cn.wenhe9.jvm.t3.load.load5_1</span><br><span class="line">bootstrap F init</span><br><span class="line">null</span><br></pre></td></tr></table></figure></div>
<ul>
<li><code>-Xbootclasspath</code>  表示设置 bootclasspath</li>
<li>其中 /a:. 表示将当前目录追加至 bootclasspath 之后</li>
<li>可以用这个方法替换核心类
<ul>
<li><code>java  -Xbootclasspath:&lt;new bootclaspath&gt;</code></li>
<li><code>java -Xbootclasspath/a:&lt;追加路径&gt;</code></li>
<li><code>java -Xbootclasspath/p:&lt;追加路径&gt;</code></li>
</ul>
</li>
</ul>
<h5 id="扩展类加载器"><a class="markdownIt-Anchor" href="#扩展类加载器">#</a> 扩展类加载器</h5>
<h6 id="双亲委派模式"><a class="markdownIt-Anchor" href="#双亲委派模式">#</a> 双亲委派模式</h6>
<p>所谓的双亲委派，就是指调用类加载器的 loadClass 方法时，查找类的规则</p>
<blockquote>
<p>注意</p>
<p>这里的双亲，翻译为上级似乎更为合适，因为他们没有继承关系</p>
</blockquote>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> Class&lt;?&gt; loadClass(String name, <span class="type">boolean</span> resolve)</span><br><span class="line">    <span class="keyword">throws</span> ClassNotFoundException</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (getClassLoadingLock(name)) &#123;</span><br><span class="line">        <span class="comment">// First, check if the class has already been loaded</span></span><br><span class="line">        Class&lt;?&gt; c = findLoadedClass(name);</span><br><span class="line">        <span class="keyword">if</span> (c == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="type">long</span> <span class="variable">t0</span> <span class="operator">=</span> System.nanoTime();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (parent != <span class="literal">null</span>) &#123;</span><br><span class="line">                    c = parent.loadClass(name, <span class="literal">false</span>);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    c = findBootstrapClassOrNull(name);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">                <span class="comment">// ClassNotFoundException thrown if class not found</span></span><br><span class="line">                <span class="comment">// from the non-null parent class loader</span></span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (c == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="comment">// If still not found, then invoke findClass in order</span></span><br><span class="line">                <span class="comment">// to find the class.</span></span><br><span class="line">                <span class="type">long</span> <span class="variable">t1</span> <span class="operator">=</span> System.nanoTime();</span><br><span class="line">                c = findClass(name);</span><br><span class="line"></span><br><span class="line">                <span class="comment">// this is the defining class loader; record the stats</span></span><br><span class="line">                PerfCounter.getParentDelegationTime().addTime(t1 - t0);</span><br><span class="line">                PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);</span><br><span class="line">                PerfCounter.getFindClasses().increment();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (resolve) &#123;</span><br><span class="line">            resolveClass(c);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> c;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h5 id="线程上下文类加载器"><a class="markdownIt-Anchor" href="#线程上下文类加载器">#</a> 线程上下文类加载器</h5>
<p>我们在使用 JDBC 时，都需要加载 Driver 驱动，不知道你注意没有，不写</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Class.forName(<span class="string">&quot;com.mysql.jdbc.Driver&quot;</span>)</span><br></pre></td></tr></table></figure></div>
<p>也是可以让 com.mysql.jdbc.Driver 正确加载的</p>
<p>让我们追踪一下源码：</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DriverManager</span> &#123;</span><br><span class="line">    <span class="comment">//注册驱动的集合</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> CopyOnWriteArrayList&lt;DriverInfo&gt; registeredDrivers = <span class="keyword">new</span> <span class="title class_">CopyOnWriteArrayList</span>&lt;&gt;();</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//初始化驱动</span></span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        loadInitialDrivers();</span><br><span class="line">        println(<span class="string">&quot;JDBC DriverManager Initialized&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>先不看别的，看看 DriverManager 的类加载器：</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">System.out.println(DriverManager.class.getClassLoader());</span><br></pre></td></tr></table></figure></div>
<p>打印 null，表示他的类加载器是 Bootstrap ClassLoader，会到 JAVA_HOME/jre/lib 下搜索类，但 JAVA_HOME/jre/lib 下显然没有 mysql-connector-java-5.1.47.jar 包，这样问题来了，在 DriverManager 的静态代码块中，怎么能正确的加载 com.mysql.jdbc.Driver 呢？</p>
<p>继续看 loadInitialDivers () 方法</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">loadInitialDrivers</span><span class="params">()</span> &#123;</span><br><span class="line">    String drivers;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        drivers = AccessController.doPrivileged(<span class="keyword">new</span> <span class="title class_">PrivilegedAction</span>&lt;String&gt;() &#123;</span><br><span class="line">            <span class="keyword">public</span> String <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> System.getProperty(<span class="string">&quot;jdbc.drivers&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">        drivers = <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// If the driver is packaged as a Service Provider, load it.</span></span><br><span class="line">    <span class="comment">// Get all the drivers through the classloader</span></span><br><span class="line">    <span class="comment">// exposed as a java.sql.Driver.class service.</span></span><br><span class="line">    <span class="comment">// ServiceLoader.load() replaces the sun.misc.Providers()</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//1)使用ServiceLoader机制加载驱动，即SPI</span></span><br><span class="line">    AccessController.doPrivileged(<span class="keyword">new</span> <span class="title class_">PrivilegedAction</span>&lt;Void&gt;() &#123;</span><br><span class="line">        <span class="keyword">public</span> Void <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">            ServiceLoader&lt;Driver&gt; loadedDrivers = ServiceLoader.load(Driver.class);</span><br><span class="line">            Iterator&lt;Driver&gt; driversIterator = loadedDrivers.iterator();</span><br><span class="line"></span><br><span class="line">            <span class="comment">/* Load these drivers, so that they can be instantiated.</span></span><br><span class="line"><span class="comment">             * It may be the case that the driver class may not be there</span></span><br><span class="line"><span class="comment">             * i.e. there may be a packaged driver with the service class</span></span><br><span class="line"><span class="comment">             * as implementation of java.sql.Driver but the actual class</span></span><br><span class="line"><span class="comment">             * may be missing. In that case a java.util.ServiceConfigurationError</span></span><br><span class="line"><span class="comment">             * will be thrown at runtime by the VM trying to locate</span></span><br><span class="line"><span class="comment">             * and load the service.</span></span><br><span class="line"><span class="comment">             *</span></span><br><span class="line"><span class="comment">             * Adding a try catch block to catch those runtime errors</span></span><br><span class="line"><span class="comment">             * if driver not available in classpath but it&#x27;s</span></span><br><span class="line"><span class="comment">             * packaged as service and that service is there in classpath.</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">            <span class="keyword">try</span>&#123;</span><br><span class="line">                <span class="keyword">while</span>(driversIterator.hasNext()) &#123;</span><br><span class="line">                    driversIterator.next();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span>(Throwable t) &#123;</span><br><span class="line">            <span class="comment">// Do nothing</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    println(<span class="string">&quot;DriverManager.initialize: jdbc.drivers = &quot;</span> + drivers);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//2）使用jdbc.drivers定义的驱动名加载驱动</span></span><br><span class="line">    <span class="keyword">if</span> (drivers == <span class="literal">null</span> || drivers.equals(<span class="string">&quot;&quot;</span>)) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    String[] driversList = drivers.split(<span class="string">&quot;:&quot;</span>);</span><br><span class="line">    println(<span class="string">&quot;number of Drivers:&quot;</span> + driversList.length);</span><br><span class="line">    <span class="keyword">for</span> (String aDriver : driversList) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            println(<span class="string">&quot;DriverManager.Initialize: loading &quot;</span> + aDriver);</span><br><span class="line">            <span class="comment">//这里的CLassloader.getSystemClassLoader()就是应用程序类加载器</span></span><br><span class="line">            Class.forName(aDriver, <span class="literal">true</span>,</span><br><span class="line">                    ClassLoader.getSystemClassLoader());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">            println(<span class="string">&quot;DriverManager.Initialize: load failed: &quot;</span> + ex);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>因为 JDK 在某些时候需要打破双亲委派的模式，就会调用应用程序类加载器去加载，否则一些类是找不到的</p>
<p>再看 1)，他就是大名鼎鼎的 Service Provider Interface（SPI）</p>
<p>约定如下，在 jar 包的 META-INF/services 包下，以接口权限定名为文件，文件内容是实现类内容</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220306085831834.png"
                      alt="image-20220306085831834"
                ></p>
<p>这样可以使用</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">ServiceLoader&lt;接口类型&gt; allImpls = ServiceLoader.load(接口类型.class);</span><br><span class="line">Iterator&lt;接口类型&gt; iter = allImpls.iterator();</span><br><span class="line"><span class="keyword">while</span>(iter.hasNext())&#123;</span><br><span class="line">	iter.hasNext();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>来得到实现类，体现的是【面向接口编程 + 解耦】的思想，在下面的一些框架中运用了此思想：</p>
<ul>
<li>
<p>JDBC</p>
</li>
<li>
<p>Servlet 初始化容器</p>
</li>
<li>
<p>Dubbo （对 SPI 进行了扩展）</p>
</li>
</ul>
<p>接着看 ServiceLoader.load 方法</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;S&gt; ServiceLoader&lt;S&gt; <span class="title function_">load</span><span class="params">(Class&lt;S&gt; service)</span>&#123;</span><br><span class="line">    <span class="comment">//获取线程上下文类加载器</span></span><br><span class="line">    <span class="type">ClassLoader</span> <span class="variable">cl</span> <span class="operator">=</span> Thread.currentThread().getContextClassLoader();</span><br><span class="line">    <span class="keyword">return</span> ServiceLoader.load(service, cl);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>线程上下文类加载是当前线程使用的类加载器，默认就是应用程序类加载器，他内部又是由 Class.forName 调用了线程上下文类加载器完成类加载，具体代码在 ServiceLoader 的内部类 LazyIterator 中：</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> S <span class="title function_">nextService</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (!hasNextService())</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NoSuchElementException</span>();</span><br><span class="line">            <span class="type">String</span> <span class="variable">cn</span> <span class="operator">=</span> nextName;</span><br><span class="line">            nextName = <span class="literal">null</span>;</span><br><span class="line">            Class&lt;?&gt; c = <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                c = Class.forName(cn, <span class="literal">false</span>, loader);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (ClassNotFoundException x) &#123;</span><br><span class="line">                fail(service,</span><br><span class="line">                     <span class="string">&quot;Provider &quot;</span> + cn + <span class="string">&quot; not found&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (!service.isAssignableFrom(c)) &#123;</span><br><span class="line">                fail(service,</span><br><span class="line">                     <span class="string">&quot;Provider &quot;</span> + cn  + <span class="string">&quot; not a subtype&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="type">S</span> <span class="variable">p</span> <span class="operator">=</span> service.cast(c.newInstance());</span><br><span class="line">                providers.put(cn, p);</span><br><span class="line">                <span class="keyword">return</span> p;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Throwable x) &#123;</span><br><span class="line">                fail(service,</span><br><span class="line">                     <span class="string">&quot;Provider &quot;</span> + cn + <span class="string">&quot; could not be instantiated&quot;</span>,</span><br><span class="line">                     x);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>();          <span class="comment">// This cannot happen</span></span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure></div>
<h5 id="自定义类加载器"><a class="markdownIt-Anchor" href="#自定义类加载器">#</a> 自定义类加载器</h5>
<p>问问自己，什么时候需要自定义类加载器</p>
<ol>
<li>想加载非 classpath 随意路径中的类文件</li>
<li>都是通过接口来使用实现，希望解耦，常用在框架设计</li>
<li>这些类予以隔离，不同应用的同类名都可以加载，不冲突，常见于 tomcat 容器</li>
</ol>
<p>步骤：</p>
<ol>
<li>继承 classLoader 父类</li>
<li>要遵从双亲委派的机制，重写 findClass 方法
<ul>
<li>注意不是重谢 loadClass 方法，否则不会走双亲委派机制</li>
</ul>
</li>
<li>读取类文件的字节码</li>
<li>调用父类的 defineClass 方法来加载类</li>
<li>使用者调用该类加载器的 loadClass 方法</li>
</ol>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Load7</span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ClassNotFoundException &#123;</span><br><span class="line">        <span class="type">MyClassLoader</span> <span class="variable">classLoader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyClassLoader</span>();</span><br><span class="line">        Class&lt;?&gt; c1 = classLoader.loadClass(<span class="string">&quot;MapImpl1&quot;</span>);</span><br><span class="line">        Class&lt;?&gt; c2 = classLoader.loadClass(<span class="string">&quot;MapImpl1&quot;</span>);</span><br><span class="line"></span><br><span class="line">        System.out.println(c1 == c2); <span class="comment">//true 第一次类加载会放在自定义的类加载器的缓存中，下次在调用在缓存中能找到，就不会重复加载了</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="type">MyClassLoader</span> <span class="variable">classLoader2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyClassLoader</span>();</span><br><span class="line">        Class&lt;?&gt; c3 = classLoader2.loadClass(<span class="string">&quot;MapImpl1&quot;</span>);</span><br><span class="line">        System.out.println(c1 == c3); <span class="comment">//false 包名类名相同，而且类加载器也是同一个，才能说这两个类是完全一致的</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyClassLoader</span> <span class="keyword">extends</span> <span class="title class_">ClassLoader</span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt; findClass(String name) <span class="keyword">throws</span> ClassNotFoundException &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">path</span> <span class="operator">=</span> <span class="string">&quot;/usr/local/lib&quot;</span> + name + <span class="string">&quot;.class&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">ByteArrayOutputStream</span> <span class="variable">os</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayOutputStream</span>();</span><br><span class="line">            Files.copy(Paths.get(path), os);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//得到字节数组</span></span><br><span class="line">            <span class="type">byte</span>[] bytes = os.toByteArray();</span><br><span class="line"></span><br><span class="line">            <span class="comment">//byte[] -&gt; *.class</span></span><br><span class="line">            <span class="keyword">return</span> defineClass(name, bytes, <span class="number">0</span>, bytes.length);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ClassNotFoundException</span>(<span class="string">&quot;类文件未找到&quot;</span>, e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h2 id="运行期优化"><a class="markdownIt-Anchor" href="#运行期优化">#</a> 运行期优化</h2>
<h3 id="即时编译"><a class="markdownIt-Anchor" href="#即时编译">#</a> 即时编译</h3>
<h4 id="分层编译"><a class="markdownIt-Anchor" href="#分层编译">#</a> 分层编译</h4>
<p>（TieredCompilation）</p>
<p>JVM 将执行状态分成了五个层次：</p>
<ul>
<li>0 层，解释执行（Interpreter）</li>
<li>1 层，使用 C1 即时编译器编译执行（不带 profiling）</li>
<li>2 层，使用 C1 即时编译器编译执行（带基本的 profiling）</li>
<li>3 层， 使用 C1 即时编译器编译执行（带完全的 profiling）</li>
<li>4 层，使用 C2 即时编译器编译执行</li>
</ul>
<blockquote>
<p>profiling 是指在运行过程中，收集一些程序执行状态的数据，例如【方法的调用次数】，【循环的会边次数】等</p>
</blockquote>
<ul>
<li>即时编译器（JIT）与解释器的区别
<ul>
<li>解释器是将字节码解释为机器码，下次即使遇到相同的字节码，仍会执行重复的解释</li>
<li>JIT 是将一些字节码编译为机器码，并存入 Code Cache，下次遇到相同的代码，直接执行，无需再编译</li>
<li>解释器是将字节码解释为针对所有平台都通用的机器码</li>
<li>JIT 行根据平台类型，生成平台特定的机器码</li>
</ul>
</li>
</ul>
<p>对于占据大部分的不常用的代码，我们无需耗费时间将其编译成机器码，而是采用解释执行的方试运行；另一方面，对于近战小部分的热点代码，我们则可以将其编译成机器码，以达到理想的运行速度。执行效率上简单比较一下 Interpreter &lt; C1 &lt; C2，总的目标是发现热点代码（hotspot 名称的由来），优化之</p>
<p>逃逸分析：发现新建的对象是否逃逸，可以使用 <code>-XX:DoEscapeAnalysis</code>  关闭</p>
<h3 id="方法内联"><a class="markdownIt-Anchor" href="#方法内联">#</a> 方法内联</h3>
<p>（Inlining）</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">square</span><span class="params">(<span class="keyword">final</span> <span class="type">int</span> i)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> i * i;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">System.out.println(square(<span class="number">9</span>));</span><br></pre></td></tr></table></figure></div>
<p>如果发现 square 是热点方法，并且长度不太长时，会进行内联，所谓的内联就是把方法内代码拷贝、黏贴到调用者的位置</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="number">9</span> * <span class="number">9</span>);</span><br></pre></td></tr></table></figure></div>
<p>还能进行常量折叠（constant folding）得到优化</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="number">81</span>);</span><br></pre></td></tr></table></figure></div>
<p><code>-XX:+UnlockDiagnosticVMoptions -XX:+PrintInlining</code>  打印针对哪些方法使用了内联</p>
<p><code>-XX:CompileCommand=donotinline,*JIT2.square</code>  禁止内联，后面是使用通配符禁止哪些方法的内联</p>
<h3 id="字段优化"><a class="markdownIt-Anchor" href="#字段优化">#</a> 字段优化</h3>
<p>尽可能使用局部变量而不是成员变量，减少循环时查询成员变量的次数，提高效率</p>
<h3 id="反射优化"><a class="markdownIt-Anchor" href="#反射优化">#</a> 反射优化</h3>
<p>默认使用 DelegatingMethodAccessorImpl 调用 NativeMethodAccessorImpl 的 invoke 方法，在调用本地方法 invoke0 进行反射方法调用</p>
<p>当某一个方法反射调用达到 15 次时，会再 NativeMethodAccessorImpl 的 invoke 方法根据调用方法的信息生成一个继承自 MethodAccessorImpl 的 MethodAccessor，并赋值给 DelegatingMethodAccessorImpl</p>
<p>当之后的该方法的反射调用触发时，就会调用生成的这个 MethodAccessor，在这个方法的 invoke 中，会直接调用该方法而不用再去调用本地方法</p>
<h2 id="内存模型"><a class="markdownIt-Anchor" href="#内存模型">#</a> 内存模型</h2>
<h3 id="java内存模型"><a class="markdownIt-Anchor" href="#java内存模型">#</a> java 内存模型</h3>
<p>简单地说，JMM 定义了一套在多线程读写共享数据时（成员变量和数组）时，对数据的可见性、有序性和原子性的规则和保障</p>
<h4 id="原子性"><a class="markdownIt-Anchor" href="#原子性">#</a> 原子性</h4>
<p>问题提出：两个线程对初始值为 0 的静态变量一个做自增，一个做自减，各做 5000 次，结果是 0 吗？</p>
<h5 id="问题分析"><a class="markdownIt-Anchor" href="#问题分析">#</a> 问题分析</h5>
<p>以上的结果可能是正数、负数、零。为什么呢？因为 Java 中对静态变量的自增、自减并不是原子操作</p>
<p>例如对于 i++ 而言（i 为静态变量），实际会产生的 JVM 字节码指令：</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">getstatic		i <span class="comment">//获取静态变量i的值</span></span><br><span class="line">iconst_1		  <span class="comment">//准备常量1</span></span><br><span class="line">iadd			  <span class="comment">//自增</span></span><br><span class="line">putstatic		i <span class="comment">//将修改后的值存入静态变量i</span></span><br></pre></td></tr></table></figure></div>
<p>而对应 i–也是类似</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">getstatic		i <span class="comment">//获取静态变量i的值</span></span><br><span class="line">iconst_1		  <span class="comment">//准备常量1</span></span><br><span class="line">isub			  <span class="comment">//自减</span></span><br><span class="line">putstatic		i <span class="comment">//将修改后的值存入静态变量i</span></span><br></pre></td></tr></table></figure></div>
<p>而 Java 的内存模型如下，完成静态变量的自增、自减需要在主存和线程中进行数据交换</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220306143623411.png"
                      alt="image-20220306143623411"
                ></p>
<p>如果是单线程以上 8 行代码是顺序执行（不会交错），没有问题</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">getstatic		i <span class="comment">//获取静态变量i的值</span></span><br><span class="line">iconst_1		  <span class="comment">//准备常量1</span></span><br><span class="line">iadd			  <span class="comment">//自增</span></span><br><span class="line">putstatic		i <span class="comment">//将修改后的值存入静态变量i</span></span><br><span class="line">getstatic		i <span class="comment">//获取静态变量i的值</span></span><br><span class="line">iconst_1		  <span class="comment">//准备常量1</span></span><br><span class="line">isub			  <span class="comment">//自减</span></span><br><span class="line">putstatic		i <span class="comment">//将修改后的值存入静态变量i</span></span><br></pre></td></tr></table></figure></div>
<p>但在多线程下这 8 行代码可能交错运行</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220306143959628.png"
                      alt="image-20220306143959628"
                ></p>
<h5 id="解决方法"><a class="markdownIt-Anchor" href="#解决方法">#</a> 解决方法</h5>
<p>synchronized (同步关键字)</p>
<p>语法</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">synchronized</span>(对象)&#123;</span><br><span class="line">    要作为原子操作的代码</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestReflect</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">static</span> <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (obj)&#123;</span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">5000</span>; j++) &#123;</span><br><span class="line">                        i++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (obj)&#123;</span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">5000</span>; j++) &#123;</span><br><span class="line">                        i--;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        t1.start();</span><br><span class="line">        t2.start();</span><br><span class="line"></span><br><span class="line">        t1.join();</span><br><span class="line">        t2.join();</span><br><span class="line"></span><br><span class="line">        System.out.println(i);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h4 id="可见性"><a class="markdownIt-Anchor" href="#可见性">#</a> 可见性</h4>
<h5 id="退不出的循环"><a class="markdownIt-Anchor" href="#退不出的循环">#</a> 退不出的循环</h5>
<p>先来看一个现象，main 线程的 run 变量的修改对于 t 线程不可见，导致了 t 线程无法停止</p>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="type">boolean</span> <span class="variable">run</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>&#123;</span><br><span class="line">    <span class="type">Thread</span> <span class="variable">t</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">while</span>(run)&#123;</span><br><span class="line">            ....</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    t.start();</span><br><span class="line">    </span><br><span class="line">    Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">    run = <span class="literal">false</span>; <span class="comment">//线程t不会如预想的停下来</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>为什么呢？分析一下</p>
<ol>
<li>初始状态，t 线程刚刚开始从主内存读取了 run 的值到工作内存
<ul>
<li><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220306150058186.png"
                      alt="image-20220306150058186"
                ></li>
</ul>
</li>
<li>因为 t 线程少频繁的从主内存中读取 run 的值，JIT 编辑器会将 run 的值缓存至自己工作内存中的高速缓存中，减少对主存中 run 的访问，提高效率
<ul>
<li><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220306150235738.png"
                      alt="image-20220306150235738"
                ></li>
</ul>
</li>
<li>1 秒之后，main 线程修改了 run 的值，并同步至主存，而 t 是从自己工作内存中的高速缓存中读取这个变量的值，结果永远是旧值
<ul>
<li><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220306150432081.png"
                      alt="image-20220306150432081"
                ></li>
</ul>
</li>
</ol>
<h5 id="解决办法"><a class="markdownIt-Anchor" href="#解决办法">#</a> 解决办法</h5>
<p>volatile（易变关键字）</p>
<p>他可以用来修饰成员变量和静态成员变量，他可以避免线程从自己的工作内存缓存中查找变量的值，必须到主存中获取他的值，线程操作 volatile 变量都是直接操作内存</p>
<h5 id="可见性-2"><a class="markdownIt-Anchor" href="#可见性-2">#</a> 可见性</h5>
<p>前面例子体现的实际就是可见性，他保证的是在多个线程之间，一个线程对 volatile 变量的修改对另一个线程可见，不能保证原子性，仅用在一个写线程，多个读线程的情况</p>
<blockquote>
<p>注意</p>
<p>synchronized 语句块既可以保证代码块的原子性，也同时保证代码内变量的可见性，但缺点是 sychronized 是属于重量级操作，性能相对较低</p>
<p>如果在前面示例的死循环中加入 System.out.println () 会发现即使不加 volatile 修饰符，线程 t 也能正确看到 run 变量的修改了</p>
</blockquote>
<h4 id="有序性"><a class="markdownIt-Anchor" href="#有序性">#</a> 有序性</h4>
<p>可能出现指令重排，使用 volatile 修饰变量</p>
<h4 id="happens-before"><a class="markdownIt-Anchor" href="#happens-before">#</a> happens-before</h4>
<p>happens-before 规定了哪些写操作对其他线程的读操作可见，他是可见性与有序性嗯对一套规则总结</p>
<ul>
<li>
<p>线程解锁 m 之前对变量的写，对于接下来对 m 加锁的其他线程对该变量的读可见</p>
<ul>
<li>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><figcaption data-lang="java"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">static</span> <span class="token keyword">int</span> x<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">static</span> <span class="token class-name">Object</span> m <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">new</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token keyword">synchronized</span><span class="token punctuation">(</span>m<span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="5"></td><td><pre>        x <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token punctuation">&#125;</span><span class="token punctuation">,</span> <span class="token string">"t1"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="8"></td><td><pre></pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token keyword">new</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>    <span class="token keyword">synchronized</span><span class="token punctuation">(</span>m<span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="11"></td><td><pre>        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="12"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="13"></td><td><pre><span class="token punctuation">&#125;</span><span class="token punctuation">,</span> <span class="token string">"t2"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="14"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">29</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>线程 start 对变量的写，对该线程开始后对该变量的读可见</p>
<ul>
<li>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><figcaption data-lang="java"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">static</span> <span class="token keyword">int</span> x<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre>x <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="4"></td><td><pre></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token keyword">new</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token punctuation">&#125;</span><span class="token punctuation">,</span> <span class="token string">"t1"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">30</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>线程 t1 打断 t2（interrupt）前对变量的写，对于其他线程得知 t2 被打断后对变量的读可见（通过 t2 的 interrupted () 或 t2.isInterrupted ()）</p>
<ul>
<li>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><figcaption data-lang="java"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> <span class="token class-name">DemoApplicationTests</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">static</span> <span class="token keyword">int</span> x<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="3"></td><td><pre></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="5"></td><td><pre>        <span class="token class-name">Thread</span> t2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="6"></td><td><pre>            <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="7"></td><td><pre>                <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">isInterrupted</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="8"></td><td><pre>                    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="9"></td><td><pre>                    <span class="token keyword">break</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>                <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="11"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="12"></td><td><pre>        <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="13"></td><td><pre>        t2<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="14"></td><td><pre></pre></td></tr><tr><td data-num="15"></td><td><pre>        <span class="token keyword">new</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="16"></td><td><pre>            <span class="token keyword">try</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="17"></td><td><pre>                <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="18"></td><td><pre>            <span class="token punctuation">&#125;</span><span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">InterruptedException</span> e<span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="19"></td><td><pre>                e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="20"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="21"></td><td><pre>            x <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="22"></td><td><pre>            t2<span class="token punctuation">.</span><span class="token function">interrupt</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="23"></td><td><pre>        <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="24"></td><td><pre></pre></td></tr><tr><td data-num="25"></td><td><pre>        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>t2<span class="token punctuation">.</span><span class="token function">isInterrupted</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="26"></td><td><pre>            <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token keyword">yield</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="27"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="28"></td><td><pre>        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="29"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="30"></td><td><pre></pre></td></tr><tr><td data-num="31"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="32"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">31</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
</ul>
<p>获取共享变量时，为了保证该变量的可见性，需要使用 volatile 修饰，结合 CAS 和 volatile 可以实现无锁并发，适用于竞争不激烈，多核 CPU 的场景下</p>
<ul>
<li>因为没有使用 sychronzied，所以线程不会陷入阻塞，这是效率提升的因素之一</li>
<li>但如果竞争激烈，可以想到尝试必然频繁发生，反而效率会受影响</li>
</ul>
<p>CAS 底层依赖于一个 UnSafe 类来直接调用操作系统底层的 CAS 指令，下面是直接使用 Unsafe 类对象进行线程安全保护的一个例子</p>
<h4 id="乐观锁和悲观锁"><a class="markdownIt-Anchor" href="#乐观锁和悲观锁">#</a> 乐观锁和悲观锁</h4>
<ul>
<li>CAS 是基于乐观锁的思想：最乐观的估计，不怕别的线程来修改共享变量，就算改了也没关系，我吃亏点再重试呗</li>
<li>synchroized 是基于悲观锁的思想：最悲观的估计，得防着其他线程来修改共享变量，我上了锁你们都别想改，我改完了解开锁，你们才有机会</li>
</ul>
<h4 id="原子操作类"><a class="markdownIt-Anchor" href="#原子操作类">#</a> 原子操作类</h4>
<p>juc（java.util.concurrent）中提供了原子操作类，可以提供线程安全的操作，例如 AtomicInteger、AtomicBoolean 等，他们底层就是采用 CAS 技术 + volatile 实现的</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20220306171823369.png"
                      alt="image-20220306171823369"
                ></p>
<h3 id="synchronized优化"><a class="markdownIt-Anchor" href="#synchronized优化">#</a> synchronized 优化</h3>
<p>Java HotSpot 虚拟机中，每个对象都有对象头（包括 class 指针和 Mark Word）。Mark Word 平时存储这个对象的哈希吗、分代年龄，当加锁时，这些信息就根据情况被替换为 标记位、线程锁记录指针、重量级锁指针、线程 ID 等内容</p>
<h4 id="轻量级锁"><a class="markdownIt-Anchor" href="#轻量级锁">#</a> 轻量级锁</h4>
<p>如果一个对象虽然有多线程访问，但多线程访问的时间是错开的（也就是没有竞争），那么可以使用轻量级锁</p>
<h4 id="锁膨胀"><a class="markdownIt-Anchor" href="#锁膨胀">#</a> 锁膨胀</h4>
<p>如果在尝试加轻量级锁的过程中，CAS 操作无法成功，这时一种情况就是有其他线程为此对象加了轻量级锁（有竞争），这时需要进行锁膨胀，将轻量级的锁变为重量级锁</p>
<h4 id="重量锁"><a class="markdownIt-Anchor" href="#重量锁">#</a> 重量锁</h4>
<p>重量锁竞争的时候，还可以使用自旋来进行优化，如果当前线程自旋成功（即这时候持锁线程已经退出了同步块，释放了锁），这时当前线程就可以避免阻塞</p>
<p>在 Java6 之后自旋是自适应的，比如对象刚刚的一次自旋操作成功过，那么认为这次自旋成功的可能性会高，就多自旋几次，反之就少自旋几次甚至不自旋</p>
<ul>
<li>自旋会占用 CPU 时间，单核 CPU 自旋就是浪费，多核 CPU 自旋才能发挥优势</li>
<li>Java 之后不能控制是否开启自旋功能</li>
</ul>
<h4 id="偏向锁"><a class="markdownIt-Anchor" href="#偏向锁">#</a> 偏向锁</h4>
<p>轻量级锁在没有竞争时（就自己这个线程），每次重入仍然需要执行 CAS 操作，Java6 中引入了偏向锁开做进一步优化：只有第一次使用 CAS 将线程 ID 设置到对象的 Mark Word 中头，之后发现这个线程 ID 是自己的就表示没有竞争，不用重新 CAS</p>
<ul>
<li>撤销偏向需要将持锁线程升级为轻量级锁，这个过程中所有线程需要暂停（STW）</li>
<li>访问对象的 hashCode 也会撤销偏向锁</li>
<li>如果对象虽然被多个线程访问，但没有竞争，这时偏向了线程 T1 的对象仍有机会重新偏向 T2，重偏向会重置对象的 ThreadID</li>
<li>撤销偏析和重偏向都是批量进行的，以类为单位</li>
<li>如果撤销偏向到达某个阈值，整个类的所有对象都会变为不可偏向的</li>
<li>可以主动使用 <code>-XX:-UseBiasedLocking</code>  禁用偏向锁</li>
</ul>
<h4 id="其他优化"><a class="markdownIt-Anchor" href="#其他优化">#</a> 其他优化</h4>
<h5 id="减少上锁时间"><a class="markdownIt-Anchor" href="#减少上锁时间">#</a> 减少上锁时间</h5>
<p>同步代码块中尽量短</p>
<h5 id="减少锁的粒度"><a class="markdownIt-Anchor" href="#减少锁的粒度">#</a> 减少锁的粒度</h5>
<p>将一个锁拆分为多个锁提高并发度</p>
<h5 id="锁粗化"><a class="markdownIt-Anchor" href="#锁粗化">#</a> 锁粗化</h5>
<p>多次循环进入同步代码块不如把同步代码块内多次循环</p>
<p>另外 JVM 可能会做如下优化，把多次 append 的加锁操作粗化为一次（因为我都是对同一个对象加锁，没必要置入多次）</p>
<h5 id="锁消除"><a class="markdownIt-Anchor" href="#锁消除">#</a> 锁消除</h5>
<p>JVM 会进行代码的逃逸分析，例如某个加锁对象是方法内局部变量，不会被其他线程锁访问到，这时候就会被即时编译器忽略掉所有同步操作</p>
<h5 id="读写分离"><a class="markdownIt-Anchor" href="#读写分离">#</a> 读写分离</h5>
<p>CopyOnWriteArrayList</p>
<p>CopyOnWri</p>

            </div>

            
                <div class="post-copyright-info">
                    <div class="article-copyright-info-container">
    <ul>
        <li><strong>标题:</strong> JVM</li>
        <li><strong>作者:</strong> meteor</li>
        <li><strong>创建于:</strong> 2023-06-05 16:45:23</li>
        
            <li>
                <strong>更新于:</strong> 2023-06-05 00:45:47
            </li>
        
        <li>
            <strong>链接:</strong> https://gitee.com/du-jinliang/2023/06/06/JVM/
        </li>
        <li>
            <strong>版权声明:</strong> 本文章采用 <a class="license" target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh">CC BY-NC-SA 4.0</a> 进行许可。
        </li>
    </ul>
</div>

                </div>
            

            
                <ul class="post-tags-box">
                    
                        <li class="tag-item">
                            <a href="/tags/JVM/">#JVM</a>&nbsp;
                        </li>
                    
                </ul>
            

            

            
                <div class="article-nav">
                    
                        <div class="article-prev">
                            <a class="prev"
                            rel="prev"
                            href="/2023/06/06/JWT/"
                            >
                                <span class="left arrow-icon flex-center">
                                    <i class="fa-solid fa-chevron-left"></i>
                                </span>
                                <span class="title flex-center">
                                    <span class="post-nav-title-item">JWT</span>
                                    <span class="post-nav-item">上一篇</span>
                                </span>
                            </a>
                        </div>
                    
                    
                        <div class="article-next">
                            <a class="next"
                            rel="next"
                            href="/2023/06/06/JSR303%E6%95%B0%E6%8D%AE%E6%A0%A1%E9%AA%8C/"
                            >
                                <span class="title flex-center">
                                    <span class="post-nav-title-item">JSR303数据校验</span>
                                    <span class="post-nav-item">下一篇</span>
                                </span>
                                <span class="right arrow-icon flex-center">
                                    <i class="fa-solid fa-chevron-right"></i>
                                </span>
                            </a>
                        </div>
                    
                </div>
            


            
                <div class="comment-container">
                    <div class="comments-container">
    <div id="comment-anchor"></div>
    <div class="comment-area-title">
        <i class="fa-solid fa-comments"></i>&nbsp;评论
    </div>
    

        
            
    <div id="gitalk-container"></div>
    <script data-pjax
            src="//cdn.jsdelivr.net/npm/gitalk/dist/gitalk.min.js"></script>
    <script data-pjax>

        function loadGitalk() {
            let __gitalk__pathname = decodeURI(location.pathname);
            const __gitalk__pathnameLength = __gitalk__pathname.length;
            const __gitalk__pathnameMaxLength = 50;
            if (__gitalk__pathnameLength > __gitalk__pathnameMaxLength) {
                __gitalk__pathname = __gitalk__pathname.substring(0, __gitalk__pathnameMaxLength - 3) + '...';
            }

            try {
                Gitalk && new Gitalk({
                    clientID: '55bad54a77b7e60ad62d',
                    clientSecret: '1031c81500c6be06e338087cb7b713f2d0201b46',
                    repo: 'wait-you.github.io',
                    owner: 'wait-you',
                    admin: ['wait-you'],
                    id: 'comment',
                    language: 'zh-CN'
                }).render('gitalk-container');

            } catch (e) {
                window.Gitalk = null;
            }
        }

        if ('true') {
            const loadGitalkTimeout = setTimeout(() => {
                loadGitalk();
                clearTimeout(loadGitalkTimeout);
            }, 1000);
        } else {
            window.addEventListener('DOMContentLoaded', loadGitalk);
        }
    </script>



        
    
</div>

                </div>
            
        </div>

        
            <div class="toc-content-container">
                <div class="post-toc-wrap">
    <div class="post-toc">
        <div class="toc-title">此页目录</div>
        <div class="page-title">JVM</div>
        <ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#jvm"><span class="nav-text"> JVM</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%BC%95%E8%A8%80"><span class="nav-text"> 引言</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFjvm"><span class="nav-text"> 什么是 JVM</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF"><span class="nav-text"> 学习路线</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84"><span class="nav-text"> 内存结构</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8"><span class="nav-text"> 程序计数器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88"><span class="nav-text"> 虚拟机栈</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%A0%88%E5%86%85%E5%AD%98%E6%BA%A2%E5%87%BA"><span class="nav-text"> 栈内存溢出</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E8%BF%90%E8%A1%8C%E8%AF%8A%E6%96%AD"><span class="nav-text"> 线程运行诊断</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%9C%AC%E5%9C%B0%E6%96%B9%E6%B3%95%E6%A0%88"><span class="nav-text"> 本地方法栈</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%A0%86"><span class="nav-text"> 堆</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%96%B9%E6%B3%95%E5%8C%BA"><span class="nav-text"> 方法区</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%BF%90%E8%A1%8C%E6%97%B6%E5%B8%B8%E9%87%8F%E6%B1%A0"><span class="nav-text"> 运行时常量池</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#stringtable"><span class="nav-text"> StringTable</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%9B%B4%E6%8E%A5%E5%86%85%E5%AD%98"><span class="nav-text"> 直接内存</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%89%A7%E8%A1%8C%E5%BC%95%E6%93%8E"><span class="nav-text"> 执行引擎</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6"><span class="nav-text"> 垃圾回收</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%9B%B8%E5%85%B3vm%E5%8F%82%E6%95%B0"><span class="nav-text"> 相关 VM 参数</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8"><span class="nav-text"> 垃圾回收器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E9%98%B6%E6%AE%B5"><span class="nav-text"> 类加载阶段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8"><span class="nav-text"> 类加载器</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%BF%90%E8%A1%8C%E6%9C%9F%E4%BC%98%E5%8C%96"><span class="nav-text"> 运行期优化</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8D%B3%E6%97%B6%E7%BC%96%E8%AF%91"><span class="nav-text"> 即时编译</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%96%B9%E6%B3%95%E5%86%85%E8%81%94"><span class="nav-text"> 方法内联</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AD%97%E6%AE%B5%E4%BC%98%E5%8C%96"><span class="nav-text"> 字段优化</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8F%8D%E5%B0%84%E4%BC%98%E5%8C%96"><span class="nav-text"> 反射优化</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B"><span class="nav-text"> 内存模型</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#java%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B"><span class="nav-text"> java 内存模型</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#synchronized%E4%BC%98%E5%8C%96"><span class="nav-text"> synchronized 优化</span></a></li></ol></li></ol></li></ol>

    </div>
</div>
            </div>
        
    </div>
</div>


                

            </div>
            
            

        </div>

        <div class="main-content-footer">
            <footer class="footer">
    <div class="info-container">
        <div class="copyright-info">
            &copy;
            
              <span>2022</span>
              -
            
            2023&nbsp;&nbsp;<i class="fa-solid fa-heart fa-beat" style="--fa-animation-duration: 0.5s; color: #f54545"></i>&nbsp;&nbsp;<a href="/">meteor</a>
        </div>
        
            <script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
            <div class="website-count info-item">
                
                    <span id="busuanzi_container_site_uv" class="busuanzi_container_site_uv">
                        访问人数&nbsp;<span id="busuanzi_value_site_uv" class="busuanzi_value_site_uv"></span>
                    </span>
                
                
                    <span id="busuanzi_container_site_pv" class="busuanzi_container_site_pv">
                        总访问量&nbsp;<span id="busuanzi_value_site_pv" class="busuanzi_value_site_pv"></span>
                    </span>
                
            </div>
        
        <div class="theme-info info-item">
            <span class="powered-by-container">由 <?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="1rem" height="1rem" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve"><path fill="#0E83CD" d="M256.4,25.8l-200,115.5L56,371.5l199.6,114.7l200-115.5l0.4-230.2L256.4,25.8z M349,354.6l-18.4,10.7l-18.6-11V275H200v79.6l-18.4,10.7l-18.6-11v-197l18.5-10.6l18.5,10.8V237h112v-79.6l18.5-10.6l18.5,10.8V354.6z"/></svg><a target="_blank" href="https://hexo.io">Hexo</a> 驱动</span>
                <br>
            <span class="theme-version-container">主题&nbsp;<a class="theme-version" target="_blank" href="https://github.com/EvanNotFound/hexo-theme-redefine">Redefine v2.1.4</a>
        </div>
        
            <div class="icp-info info-item"><a target="_blank" rel="nofollow" href="
                
                    beian.miit.gov.cn
                
                ">冀ICP备20010108号</a></div>
        
        
        
            <div id="start_div" style="display:none">
                2022/8/17 11:45:14
            </div>
            <div>
                博客已运行 <span class="odometer" id="runtime_days" ></span> 天 <span class="odometer" id="runtime_hours"></span> 小时 <span class="odometer" id="runtime_minutes"></span> 分钟 <span class="odometer" id="runtime_seconds"></span> 秒
            </div>
        
        
        
            <script async data-pjax>
                try {
                    function odometer_init() {
                    const elements = document.querySelectorAll('.odometer');
                    elements.forEach(el => {
                        new Odometer({
                            el,
                            format: '( ddd).dd',
                            duration: 200
                        });
                    });
                    }
                    odometer_init();
                } catch (error) {}
            </script>
        
        
        
    </div>  
</footer>
        </div>
    </div>

    
        <div class="post-tools">
            <div class="post-tools-container">
    <ul class="article-tools-list">
        <!-- TOC aside toggle -->
        
            <li class="right-bottom-tools page-aside-toggle">
                <i class="fa-regular fa-outdent"></i>
            </li>
        

        <!-- go comment -->
        
            <li class="go-comment">
                <i class="fa-regular fa-comments"></i>
            </li>
        
    </ul>
</div>

        </div>
    

    <div class="right-side-tools-container">
        <div class="side-tools-container">
    <ul class="hidden-tools-list">
        <li class="right-bottom-tools tool-font-adjust-plus flex-center">
            <i class="fa-regular fa-magnifying-glass-plus"></i>
        </li>

        <li class="right-bottom-tools tool-font-adjust-minus flex-center">
            <i class="fa-regular fa-magnifying-glass-minus"></i>
        </li>

        <li class="right-bottom-tools tool-expand-width flex-center">
            <i class="fa-regular fa-expand"></i>
        </li>

        <li class="right-bottom-tools tool-dark-light-toggle flex-center">
            <i class="fa-regular fa-moon"></i>
        </li>

        <!-- rss -->
        

        

        <li class="right-bottom-tools tool-scroll-to-bottom flex-center">
            <i class="fa-regular fa-arrow-down"></i>
        </li>
    </ul>

    <ul class="visible-tools-list">
        <li class="right-bottom-tools toggle-tools-list flex-center">
            <i class="fa-regular fa-cog fa-spin"></i>
        </li>
        
            <li class="right-bottom-tools tool-scroll-to-top flex-center">
                <i class="arrow-up fas fa-arrow-up"></i>
                <span class="percent"></span>
            </li>
        
        
    </ul>
</div>

    </div>

    <div class="image-viewer-container">
    <img src="">
</div>


    


</main>




<script src="/js/utils.js"></script>

<script src="/js/main.js"></script>

<script src="/js/layouts/navbarShrink.js"></script>

<script src="/js/tools/scrollTopBottom.js"></script>

<script src="/js/tools/lightDarkSwitch.js"></script>





    
<script src="/js/tools/codeBlock.js"></script>




    
<script src="/js/layouts/lazyload.js"></script>




    
<script src="/js/tools/runtime.js"></script>

    
<script src="/js/layouts/odometer.min.js"></script>

    
<link rel="stylesheet" href="/assets/odometer-theme-minimal.css">




  
<script src="/js/libs/Typed.min.js"></script>

  
<script src="/js/plugins/typed.js"></script>







<div class="post-scripts pjax">
    
        
<script src="/js/tools/tocToggle.js"></script>

<script src="/js/libs/anime.min.js"></script>

<script src="/js/layouts/toc.js"></script>

<script src="/js/plugins/tabs.js"></script>

    
</div>


    
<script src="/js/libs/pjax.min.js"></script>

<script>
    window.addEventListener('DOMContentLoaded', () => {
        window.pjax = new Pjax({
            selectors: [
                'head title',
                '.page-container',
                '.pjax',
            ],
            history: true,
            debug: false,
            cacheBust: false,
            timeout: 0,
            analytics: false,
            currentUrlFullReload: false,
            scrollRestoration: false,
            // scrollTo: true,
        });

        document.addEventListener('pjax:send', () => {
            Global.utils.pjaxProgressBarStart();
        });

        document.addEventListener('pjax:complete', () => {
            Global.utils.pjaxProgressBarEnd();
            window.pjax.executeScripts(document.querySelectorAll('script[data-pjax], .pjax script'));
            Global.refresh();
        });
    });
</script>




</body>
</html>
